Initial commit of Amber for open source
Amber is a multi-API shader test framework.
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.
* A single text string (or file) maps to a single graphics API pipeline test case. The text includes:
* Input data, including buffers and images.
* Shaders.
* Expectations for the result of running the pipeline.
* Shaders can be expressed in binary form (as hex), in SPIR-V assembly, or in a higher level shader language.
* After executing the pipeline, result buffers and images can be saved to output files.
This is not an officially supported Google product.
diff --git a/src/vulkan/CMakeLists.txt b/src/vulkan/CMakeLists.txt
new file mode 100644
index 0000000..8df5759
--- /dev/null
+++ b/src/vulkan/CMakeLists.txt
@@ -0,0 +1,43 @@
+# Copyright 2018 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(VULKAN_ENGINE_SOURCES
+ bit_copy.cc
+ buffer.cc
+ command.cc
+ descriptor.cc
+ device.cc
+ engine_vulkan.cc
+ format_data.cc
+ frame_buffer.cc
+ resource.cc
+ image.cc
+ pipeline.cc
+ graphics_pipeline.cc
+ vertex_buffer.cc
+)
+
+add_library(libamberenginevulkan ${VULKAN_ENGINE_SOURCES})
+amber_default_compile_options(libamberenginevulkan)
+set_target_properties(libamberenginevulkan PROPERTIES
+ OUTPUT_NAME "amberenginevulkan"
+)
+target_link_libraries(libamberenginevulkan ${VULKAN_LIB})
+
+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
+ # with XCode 10.
+ target_compile_options(libamberenginevulkan PRIVATE -Wno-zero-as-null-pointer-constant)
+endif()
diff --git a/src/vulkan/bit_copy.cc b/src/vulkan/bit_copy.cc
new file mode 100644
index 0000000..3be826f
--- /dev/null
+++ b/src/vulkan/bit_copy.cc
@@ -0,0 +1,166 @@
+// Copyright 2018 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/bit_copy.h"
+
+#include <cassert>
+#include <cstring>
+
+namespace amber {
+namespace vulkan {
+
+// static
+void BitCopy::ShiftBufferBits(uint8_t* buffer,
+ uint8_t length_bytes,
+ uint8_t shift_bits) {
+ if (shift_bits == 0)
+ return;
+
+ assert(shift_bits < 8);
+
+ uint8_t carry = 0;
+ for (uint32_t i = 0; i < length_bytes; ++i) {
+ uint8_t new_value = static_cast<uint8_t>(buffer[i] << shift_bits) | carry;
+ carry = buffer[i] >> (8 - shift_bits);
+ buffer[i] = new_value;
+ }
+}
+
+// static
+void BitCopy::CopyValueToBuffer(uint8_t* dst,
+ const Value& src,
+ uint8_t bit_offset,
+ uint8_t bits) {
+ uint8_t data[9] = {};
+ if (src.IsInteger()) {
+ if (bits <= 8) {
+ uint8_t data_uint8 = src.AsUint8();
+ data[0] = data_uint8;
+ } else if (bits <= 16) {
+ uint16_t data_uint16 = src.AsUint16();
+ std::memcpy(data, &data_uint16, sizeof(uint16_t));
+ } else if (bits <= 32) {
+ uint32_t data_uint32 = src.AsUint32();
+ std::memcpy(data, &data_uint32, sizeof(uint32_t));
+ } else if (bits <= 64) {
+ uint64_t data_uint64 = src.AsUint64();
+ std::memcpy(data, &data_uint64, sizeof(uint64_t));
+ } else {
+ assert(false && "Invalid int bits for CopyBits");
+ }
+ } else {
+ if (bits == 64) {
+ double data_double = src.AsDouble();
+ std::memcpy(data, &data_double, sizeof(double));
+ } else {
+ float data_float = src.AsFloat();
+ uint16_t hex_float = 0;
+ switch (bits) {
+ case 32:
+ std::memcpy(data, &data_float, sizeof(float));
+ break;
+ case 16:
+ case 11:
+ case 10:
+ hex_float = FloatToHexFloat(data_float, bits);
+ std::memcpy(data, &hex_float, sizeof(uint16_t));
+ break;
+ default:
+ assert(false && "Invalid float bits for CopyBits");
+ break;
+ }
+ }
+ }
+
+ while (bit_offset > 7) {
+ ++dst;
+ bit_offset -= 8;
+ }
+
+ ShiftBufferBits(data, ((bit_offset + bits - 1) / 8) + 1, bit_offset);
+ CopyBits(dst, data, bit_offset, bits);
+}
+
+// static
+void BitCopy::CopyBits(uint8_t* dst,
+ const uint8_t* src,
+ uint8_t bit_offset,
+ uint8_t bits) {
+ while (bit_offset + bits > 0) {
+ uint8_t target_bits = bits;
+ if (bit_offset + target_bits > 8)
+ target_bits = 8 - bit_offset;
+
+ uint8_t bit_mask =
+ static_cast<uint8_t>(((1U << target_bits) - 1U) << bit_offset);
+ *dst = (*src & bit_mask) | (*dst & ~bit_mask);
+
+ bit_offset -= bit_offset;
+ bits -= target_bits;
+ ++dst;
+ ++src;
+ }
+}
+
+// static
+uint16_t BitCopy::FloatExponent(const uint32_t hex_float) {
+ uint32_t exponent = ((hex_float >> 23U) & ((1U << 8U) - 1U)) - 127U + 15U;
+ const uint32_t half_exponent_mask = (1U << 5U) - 1U;
+ assert((exponent & ~half_exponent_mask) == 0U);
+ return static_cast<uint16_t>(exponent & half_exponent_mask);
+}
+
+// static
+uint16_t BitCopy::FloatToHexFloat(float value, uint8_t bits) {
+ switch (bits) {
+ case 10:
+ return FloatToHexFloat10(value);
+ case 11:
+ return FloatToHexFloat11(value);
+ case 16:
+ return FloatToHexFloat16(value);
+ }
+
+ assert(false && "Invalid bits");
+ return 0;
+}
+
+// static
+uint16_t BitCopy::FloatToHexFloat16(const float value) {
+ uint32_t hex;
+ memcpy(&hex, &value, sizeof(float));
+ return static_cast<uint16_t>(FloatSign(hex) << 15U) |
+ static_cast<uint16_t>(FloatExponent(hex) << 10U) | FloatMantissa(hex);
+}
+
+// static
+uint16_t BitCopy::FloatToHexFloat11(const float value) {
+ uint32_t hex;
+ memcpy(&hex, &value, sizeof(float));
+ assert(FloatSign(hex) == 0);
+ return static_cast<uint16_t>(FloatExponent(hex) << 6U) |
+ static_cast<uint16_t>(FloatMantissa(hex) >> 4U);
+}
+
+// static
+uint16_t BitCopy::FloatToHexFloat10(const float value) {
+ uint32_t hex;
+ memcpy(&hex, &value, sizeof(float));
+ assert(FloatSign(hex) == 0);
+ return static_cast<uint16_t>(FloatExponent(hex) << 5U) |
+ static_cast<uint16_t>(FloatMantissa(hex) >> 5U);
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/bit_copy.h b/src/vulkan/bit_copy.h
new file mode 100644
index 0000000..31b19bc
--- /dev/null
+++ b/src/vulkan/bit_copy.h
@@ -0,0 +1,82 @@
+// Copyright 2018 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_BIT_COPY_H_
+#define SRC_VULKAN_BIT_COPY_H_
+
+#include "src/value.h"
+
+namespace amber {
+namespace vulkan {
+
+class BitCopy {
+ public:
+ // Copy [0, bits) bits of src to [bit_offset, bit_offset + bits) of dst.
+ static void CopyValueToBuffer(uint8_t* dst,
+ const Value& src,
+ uint8_t bit_offset,
+ uint8_t bits);
+
+ private:
+ BitCopy() = delete;
+
+ ~BitCopy() = delete;
+
+ static void ShiftBufferBits(uint8_t* buffer,
+ uint8_t length_bytes,
+ uint8_t shift_bits);
+
+ static void CopyBits(uint8_t* dst,
+ const uint8_t* src,
+ uint8_t bit_offset,
+ uint8_t bits);
+
+ // Convert float to small float format.
+ // 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 but it does not exist in Vulkan.
+ //
+ // For example, 1234 in 32 bits float = 1.0011010010 * 2^10 with base 2.
+ //
+ // 1.0011010010 * 2^10 --> 0 (sign) / 10 + 127 (exp) / 0011010010 (Mantissa)
+ // --> 0x449a4000
+ static uint16_t FloatToHexFloat(float value, uint8_t bits);
+ static uint16_t FloatToHexFloat16(const float value);
+ static uint16_t FloatToHexFloat11(const float value);
+ static uint16_t FloatToHexFloat10(const float value);
+
+ static uint16_t FloatSign(const uint32_t hex_float) {
+ return static_cast<uint16_t>(hex_float >> 31U);
+ }
+
+ static uint16_t FloatExponent(const uint32_t hex_float);
+
+ static uint16_t FloatMantissa(const uint32_t hex_float) {
+ return static_cast<uint16_t>((hex_float & ((1U << 23U) - 1U)) >> 13U);
+ }
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_BIT_COPY_H_
diff --git a/src/vulkan/bit_copy_test.cc b/src/vulkan/bit_copy_test.cc
new file mode 100644
index 0000000..b9367c5
--- /dev/null
+++ b/src/vulkan/bit_copy_test.cc
@@ -0,0 +1,382 @@
+// Copyright 2018 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/bit_copy.h"
+
+#include <cstring>
+#include <type_traits>
+
+#include "gtest/gtest.h"
+#include "src/value.h"
+
+namespace amber {
+namespace vulkan {
+namespace {
+
+template <typename T>
+void ExpectBitsEQ(const uint8_t* actual, T expected) {
+ const T* ptr = reinterpret_cast<const T*>(actual);
+ EXPECT_EQ(*ptr, expected);
+}
+
+} // namespace
+
+using BitCopyTest = testing::Test;
+
+TEST_F(BitCopyTest, CopyInt8) {
+ Value value;
+ uint8_t data = 0;
+
+ // 7 0 0 7
+ // 00000000 --> 11011100 (220)
+ // 110111 (55)
+ value.SetIntValue(55);
+ BitCopy::CopyValueToBuffer(&data, value, 2, 6);
+ EXPECT_EQ(data, 220);
+
+ // 7 0 0 7
+ // 11011100 --> 11011111 (223)
+ // 11 (3)
+ value.SetIntValue(3);
+ BitCopy::CopyValueToBuffer(&data, value, 0, 2);
+ EXPECT_EQ(data, 223);
+
+ // 7 0 0 7
+ // 11011111 --> 10110111 (183)
+ // 011011 (27)
+ value.SetIntValue(27);
+ BitCopy::CopyValueToBuffer(&data, value, 1, 6);
+ EXPECT_EQ(data, 183);
+
+ // 7 0 0 7
+ // 10110111 --> 11010111 (215)
+ // 1010 (10)
+ value.SetIntValue(10);
+ BitCopy::CopyValueToBuffer(&data, value, 3, 4);
+ EXPECT_EQ(data, 215);
+}
+
+TEST_F(BitCopyTest, CopyInt16) {
+ Value value;
+ uint8_t data[2] = {};
+
+ // 15 0 15 0
+ // 0000000000000000 --> 1100000011100100 (49380)
+ // 11000000111001 (12345)
+ value.SetIntValue(12345);
+ BitCopy::CopyValueToBuffer(data, value, 2, 14);
+ ExpectBitsEQ<uint16_t>(data, 49380);
+
+ // 15 0 15 0
+ // 1100000011100100 --> 1110100000100100 (59428)
+ // 101000001 (321)
+ value.SetIntValue(321);
+ BitCopy::CopyValueToBuffer(data, value, 5, 9);
+ ExpectBitsEQ<uint16_t>(data, 59428);
+
+ // 15 0 15 0
+ // 1110100000100100 --> 1111000111010111 (61911)
+ // 1000111010111 (4567)
+ value.SetIntValue(4567);
+ BitCopy::CopyValueToBuffer(data, value, 0, 13);
+ ExpectBitsEQ<uint16_t>(data, 61911);
+
+ // 15 0 15 0
+ // 1111000111010111 --> 1001101111011111 (39903)
+ // 001101111011 (891)
+ value.SetIntValue(891);
+ BitCopy::CopyValueToBuffer(data, value, 3, 12);
+ ExpectBitsEQ<uint16_t>(data, 39903);
+}
+
+TEST_F(BitCopyTest, CopyInt32) {
+ Value value;
+ uint8_t data[4] = {};
+
+ // 31 31
+ // 0000000000000000 --> 0001011110001100
+ // 0000000000000000 0010100111000000 (395061696)
+ // 0 0
+ //
+ // 1011110001100
+ // 00101001110 (12345678)
+ value.SetIntValue(12345678);
+ BitCopy::CopyValueToBuffer(data, value, 5, 24);
+ ExpectBitsEQ<uint32_t>(data, 395061696);
+
+ // 31 31
+ // 0001011110001100 --> 0001011110000001
+ // 0010100111000000 1100110111000000 (394382784)
+ // 0 0
+ //
+ // 110000001
+ // 11001101 (98765)
+ value.SetIntValue(98765);
+ BitCopy::CopyValueToBuffer(data, value, 8, 17);
+ ExpectBitsEQ<uint32_t>(data, 394382784);
+
+ // 31 31
+ // 0001011110000001 --> 0001011110000001
+ // 1100110111000000 1100111010001110 (394382990)
+ // 0 0
+ //
+ // 1010001110 (654)
+ value.SetIntValue(654);
+ BitCopy::CopyValueToBuffer(data, value, 0, 10);
+ ExpectBitsEQ<uint32_t>(data, 394382990);
+
+ // 31 31
+ // 0001011110000001 --> 1101001011111100
+ // 1100111010001110 0101011010001110 (3539752590)
+ // 0 0
+ //
+ // 1101001011111100
+ // 010101 (654)
+ value.SetIntValue(3456789);
+ BitCopy::CopyValueToBuffer(data, value, 10, 22);
+ ExpectBitsEQ<uint32_t>(data, 3539752590);
+}
+
+TEST_F(BitCopyTest, CopyInt64) {
+ Value value;
+ uint8_t data[8] = {};
+
+ // 63 63
+ // 0000000000000000 --> 0010001111101110
+ // 0000000000000000 0011111101100110
+ // 0000000000000000 0001011110101100
+ // 0000000000000000 0000000000000000 (2589076543500976128)
+ // 0 0
+ //
+ // 10001111101110
+ // 0011111101100110
+ // 00010111101011
+ // (9876543210987)
+ value.SetIntValue(9876543210987UL);
+ BitCopy::CopyValueToBuffer(data, value, 18, 44);
+ ExpectBitsEQ<uint64_t>(data, 2589076543500976128UL);
+
+ // 63 63
+ // 0010001111101110 --> 0011110001001110
+ // 0011111101100110 1111110000011110
+ // 0001011110101100 1111010011010001
+ // 0000000000000000 0101111101011000 (4345687900345687896)
+ // 0 0
+ //
+ // 11110001001110
+ // 1111110000011110
+ // 1111010011010001
+ // 0101111101011 (543210987543210987)
+ value.SetIntValue(543210987543210987UL);
+ BitCopy::CopyValueToBuffer(data, value, 3, 59);
+ ExpectBitsEQ<uint64_t>(data, 4345687900345687896UL);
+
+ // 63 63
+ // 0011110001001110 --> 0011110001001110
+ // 1111110000011110 1001011111100010
+ // 1111010011010001 1011010011010001
+ // 0101111101011000 0101111101011000 (4345577690411130712)
+ // 0 0
+ //
+ // 110
+ // 1001011111100010
+ // 101
+ // (3456789)
+ value.SetIntValue(3456789UL);
+ BitCopy::CopyValueToBuffer(data, value, 29, 22);
+ ExpectBitsEQ<uint64_t>(data, 4345577690411130712UL);
+}
+
+TEST_F(BitCopyTest, CopyIntMultiple) {
+ uint8_t data[32] = {};
+ Value value;
+
+ // Fill [0, 32) bits of data with
+ // 11(3) / 0010001111(143) / 0001000011(67) / 1000010001(529)
+ value.SetIntValue(529);
+ BitCopy::CopyValueToBuffer(data, value, 0, 10);
+ value.SetIntValue(67);
+ BitCopy::CopyValueToBuffer(data, value, 10, 10);
+ value.SetIntValue(143);
+ BitCopy::CopyValueToBuffer(data, value, 20, 10);
+ value.SetIntValue(3);
+ BitCopy::CopyValueToBuffer(data, value, 30, 2);
+
+ // Fill [32, 96) bits of data with
+ // 00000111010110111100110100010101(123456789) /
+ // 00000000100101101011010000111111(9876543)
+ value.SetIntValue(9876543);
+ BitCopy::CopyValueToBuffer(data, value, 32, 32);
+ value.SetIntValue(123456789);
+ BitCopy::CopyValueToBuffer(data, value, 64, 32);
+
+ // Fill [96, 120) bits of data with
+ // 00011111(31) / 00001001(9) / 01001101(77)
+ value.SetIntValue(77);
+ BitCopy::CopyValueToBuffer(data, value, 96, 8);
+ value.SetIntValue(9);
+ BitCopy::CopyValueToBuffer(data, value, 104, 8);
+ value.SetIntValue(31);
+ BitCopy::CopyValueToBuffer(data, value, 112, 8);
+
+ // Fill [120, 184) bits of data with
+ // 00000001101101101001101101001011
+ // 10100110001100001111001101001110(123456789012345678)
+ value.SetIntValue(123456789012345678UL);
+ BitCopy::CopyValueToBuffer(data, value, 120, 64);
+
+ // Fill [184, 216) bits of data with
+ // 10000011110111011011010010000000(34567890)
+ value.SetIntValue(34567890);
+ BitCopy::CopyValueToBuffer(data, value, 184, 32);
+
+ // Fill [216, 256) bits of data with
+ // 01100011(99) / 1000001000110101(33333) / 11011110(222) / 01101111(111)
+ value.SetIntValue(111);
+ BitCopy::CopyValueToBuffer(data, value, 216, 8);
+ value.SetIntValue(222);
+ BitCopy::CopyValueToBuffer(data, value, 224, 8);
+ value.SetIntValue(33333);
+ BitCopy::CopyValueToBuffer(data, value, 232, 16);
+ value.SetIntValue(99);
+ BitCopy::CopyValueToBuffer(data, value, 248, 8);
+
+ // [0, 32) bits of data
+ ExpectBitsEQ<uint32_t>(data, 3371240977);
+
+ // [32, 96) bits of data
+ ExpectBitsEQ<uint64_t>(&data[4], 530242871234049087UL);
+
+ // [96, 120) bits of data
+ ExpectBitsEQ<uint16_t>(&data[12], 2381);
+ EXPECT_EQ(data[14], 31);
+
+ // [120, 184) bits of data
+ ExpectBitsEQ<uint64_t>(&data[15], 123456789012345678UL);
+
+ // [184, 216) bits of data
+ ExpectBitsEQ<uint32_t>(&data[23], 34567890);
+
+ // [216, 256) bits of data
+ ExpectBitsEQ<uint32_t>(&data[27], 2184568431);
+ EXPECT_EQ(data[31], 99);
+}
+
+TEST_F(BitCopyTest, CopyFloat16) {
+ uint8_t data[2] = {};
+ Value value;
+
+ // 16 bits
+ // Sig / Exp / Mantissa Sig / Exp / Mantissa
+ // 12.34 = 0 / 130 / 4550820 --> 0 / 18 / 555
+ value.SetDoubleValue(12.34);
+ data[0] = 0;
+ data[1] = 0;
+ BitCopy::CopyValueToBuffer(data, value, 0, 16);
+ ExpectBitsEQ<uint16_t>(data, 18987);
+
+ // 11 bits
+ // Sig / Exp / Mantissa Sig / Exp / Mantissa
+ // 5.67 = 0 / 129 / 3502244 --> 17 / 26
+ value.SetDoubleValue(5.67);
+ data[0] = 0;
+ data[1] = 0;
+ BitCopy::CopyValueToBuffer(data, value, 3, 11);
+ ExpectBitsEQ<uint16_t>(data, 8912);
+
+ // 10 bits
+ // Sig / Exp / Mantissa Sig / Exp / Mantissa
+ // 0.89 = 0 / 126 / 6543114 --> 14 / 24
+ value.SetDoubleValue(0.89);
+ data[0] = 0;
+ data[1] = 0;
+ BitCopy::CopyValueToBuffer(data, value, 2, 10);
+ ExpectBitsEQ<uint16_t>(data, 1888);
+}
+
+TEST_F(BitCopyTest, CopyFloat) {
+ uint8_t data[4] = {};
+ Value value;
+
+ // 16 bits
+ // Sig / Exp / Mantissa
+ // 12.34 = 0 / 130 / 4550820
+ value.SetDoubleValue(12.34);
+ BitCopy::CopyValueToBuffer(data, value, 0, 32);
+ ExpectBitsEQ<uint32_t>(data, 1095069860);
+
+ // 11 bits
+ // Sig / Exp / Mantissa
+ // 5.67 = 0 / 129 / 3502244
+ value.SetDoubleValue(5.67);
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0;
+ BitCopy::CopyValueToBuffer(data, value, 0, 32);
+ ExpectBitsEQ<uint32_t>(data, 1085632676);
+
+ // 10 bits
+ // Sig / Exp / Mantissa
+ // 0.89 = 0 / 126 / 6543114
+ value.SetDoubleValue(0.89);
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0;
+ BitCopy::CopyValueToBuffer(data, value, 0, 32);
+ ExpectBitsEQ<uint32_t>(data, 1063507722);
+}
+
+TEST_F(BitCopyTest, CopyDouble) {
+ uint8_t data[8] = {};
+ Value value;
+
+ // Sig / Exp / Mantissa
+ // 12.34 = 0 / 1026 / 2443202797848494
+ value.SetDoubleValue(12.34);
+ BitCopy::CopyValueToBuffer(data, value, 0, 64);
+ ExpectBitsEQ<uint64_t>(data, 4623136420479977390);
+
+ // Sig / Exp / Mantissa
+ // 5.67 = 0 / 1025 / 1880252844427182
+ value.SetDoubleValue(5.67);
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0;
+ data[4] = 0;
+ data[5] = 0;
+ data[6] = 0;
+ data[7] = 0;
+ BitCopy::CopyValueToBuffer(data, value, 0, 64);
+ ExpectBitsEQ<uint64_t>(data, 4618069870899185582);
+
+ // Sig / Exp / Mantissa
+ // 0.89 = 0 / 1022 / 3512807709348987
+ value.SetDoubleValue(0.89);
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0;
+ data[4] = 0;
+ data[5] = 0;
+ data[6] = 0;
+ data[7] = 0;
+ BitCopy::CopyValueToBuffer(data, value, 0, 64);
+ ExpectBitsEQ<uint64_t>(data, 4606191626881995899);
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/buffer.cc b/src/vulkan/buffer.cc
new file mode 100644
index 0000000..4c7d2fe
--- /dev/null
+++ b/src/vulkan/buffer.cc
@@ -0,0 +1,93 @@
+// Copyright 2018 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.h"
+
+namespace amber {
+namespace vulkan {
+
+Buffer::Buffer(VkDevice device,
+ size_t size,
+ const VkPhysicalDeviceMemoryProperties& properties)
+ : Resource(device, size, properties) {}
+
+Buffer::~Buffer() = default;
+
+Result Buffer::Initialize(const VkBufferUsageFlags usage) {
+ Result r = CreateVkBuffer(&buffer_, usage);
+ if (!r.IsSuccess())
+ return r;
+
+ AllocateResult allocate_result = AllocateAndBindMemoryToVkBuffer(
+ buffer_, &memory_, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, false);
+ if (!allocate_result.r.IsSuccess())
+ return allocate_result.r;
+
+ if (CheckMemoryHostAccessible(allocate_result.memory_type_index)) {
+ is_buffer_host_accessible_ = true;
+ return MapMemory(memory_);
+ }
+
+ is_buffer_host_accessible_ = false;
+ return Resource::Initialize();
+}
+
+Result Buffer::CreateVkBufferView(VkFormat format) {
+ VkBufferViewCreateInfo buffer_view_info = {};
+ 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 (vkCreateBufferView(GetDevice(), &buffer_view_info, nullptr, &view_) !=
+ VK_SUCCESS) {
+ return Result("Vulkan::Calling vkCreateBufferView Fail");
+ }
+
+ return {};
+}
+
+void Buffer::CopyToDevice(VkCommandBuffer command) {
+ if (is_buffer_host_accessible_)
+ return;
+
+ VkBufferCopy region = {};
+ region.srcOffset = 0;
+ region.dstOffset = 0;
+ region.size = GetSize();
+
+ vkCmdCopyBuffer(command, GetHostAccessibleBuffer(), buffer_, 1, ®ion);
+}
+
+void Buffer::Shutdown() {
+ // TODO(jaebaek): Doublecheck what happens if |view_| is VK_NULL_HANDLE on
+ // Android and Windows.
+ if (view_ != VK_NULL_HANDLE) {
+ vkDestroyBufferView(GetDevice(), view_, nullptr);
+ view_ = VK_NULL_HANDLE;
+ }
+
+ if (buffer_ != VK_NULL_HANDLE) {
+ vkDestroyBuffer(GetDevice(), buffer_, nullptr);
+ buffer_ = VK_NULL_HANDLE;
+ }
+
+ if (memory_ != VK_NULL_HANDLE) {
+ vkFreeMemory(GetDevice(), memory_, nullptr);
+ memory_ = VK_NULL_HANDLE;
+ }
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/buffer.h b/src/vulkan/buffer.h
new file mode 100644
index 0000000..9ec1dd2
--- /dev/null
+++ b/src/vulkan/buffer.h
@@ -0,0 +1,60 @@
+// Copyright 2018 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_H_
+#define SRC_VULKAN_BUFFER_H_
+
+#include "amber/result.h"
+#include "src/vulkan/resource.h"
+#include "vulkan/vulkan.h"
+
+namespace amber {
+namespace vulkan {
+
+class Buffer : public Resource {
+ public:
+ Buffer(VkDevice device,
+ size_t size,
+ const VkPhysicalDeviceMemoryProperties& properties);
+ ~Buffer() override;
+
+ Result Initialize(const VkBufferUsageFlags usage);
+ VkBuffer GetVkBuffer() const { return buffer_; }
+ Result CreateVkBufferView(VkFormat format);
+ VkBufferView GetVkBufferView() const { return view_; }
+
+ // TODO(jaebaek): Determine copy all or partial data
+ void CopyToDevice(VkCommandBuffer command);
+
+ // Resource
+ VkDeviceMemory GetHostAccessMemory() const override {
+ if (is_buffer_host_accessible_)
+ return memory_;
+
+ return Resource::GetHostAccessMemory();
+ }
+
+ void Shutdown() override;
+
+ private:
+ VkBuffer buffer_ = VK_NULL_HANDLE;
+ VkBufferView view_ = VK_NULL_HANDLE;
+ VkDeviceMemory memory_ = VK_NULL_HANDLE;
+ bool is_buffer_host_accessible_ = false;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_BUFFER_H_
diff --git a/src/vulkan/command.cc b/src/vulkan/command.cc
new file mode 100644
index 0000000..9ff205f
--- /dev/null
+++ b/src/vulkan/command.cc
@@ -0,0 +1,131 @@
+// Copyright 2018 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/command.h"
+
+#include <memory>
+
+#include "src/make_unique.h"
+
+namespace amber {
+
+namespace vulkan {
+
+CommandPool::CommandPool(VkDevice device) : device_(device) {}
+
+CommandPool::~CommandPool() = default;
+
+Result CommandPool::Initialize(uint32_t queue_family_index) {
+ VkCommandPoolCreateInfo pool_info = {};
+ pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+ pool_info.queueFamilyIndex = queue_family_index;
+
+ if (vkCreateCommandPool(device_, &pool_info, nullptr, &pool_) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkCreateCommandPool Fail");
+
+ return {};
+}
+
+void CommandPool::Shutdown() {
+ vkDestroyCommandPool(device_, pool_, nullptr);
+}
+
+CommandBuffer::CommandBuffer(VkDevice device, VkCommandPool pool, VkQueue queue)
+ : device_(device), pool_(pool), queue_(queue) {}
+
+CommandBuffer::~CommandBuffer() = default;
+
+Result CommandBuffer::Initialize() {
+ VkCommandBufferAllocateInfo command_info = {};
+ command_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ command_info.commandPool = pool_;
+ command_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ command_info.commandBufferCount = 1;
+
+ if (vkAllocateCommandBuffers(device_, &command_info, &command_) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkAllocateCommandBuffers Fail");
+
+ VkFenceCreateInfo fence_info = {};
+ fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+ if (vkCreateFence(device_, &fence_info, nullptr, &fence_) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkCreateFence Fail");
+
+ return {};
+}
+
+Result CommandBuffer::BeginIfNotInRecording() {
+ if (state_ == CommandBufferState::kRecording)
+ return {};
+
+ if (state_ != CommandBufferState::kInitial)
+ return Result("Vulkan::Begin CommandBuffer from Not Valid State");
+
+ VkCommandBufferBeginInfo command_begin_info = {};
+ command_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ command_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+ if (vkBeginCommandBuffer(command_, &command_begin_info) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkBeginCommandBuffer Fail");
+
+ state_ = CommandBufferState::kRecording;
+ return {};
+}
+
+Result CommandBuffer::End() {
+ if (state_ != CommandBufferState::kRecording)
+ return Result("Vulkan::End CommandBuffer from Not Valid State");
+
+ if (vkEndCommandBuffer(command_) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkEndCommandBuffer Fail");
+
+ state_ = CommandBufferState::kExecutable;
+ return {};
+}
+
+Result CommandBuffer::SubmitAndReset() {
+ if (state_ != CommandBufferState::kExecutable)
+ return Result("Vulkan::Submit CommandBuffer from Not Valid State");
+
+ if (vkResetFences(device_, 1, &fence_) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkResetFences Fail");
+
+ VkSubmitInfo submit_info = {};
+ submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submit_info.commandBufferCount = 1;
+ submit_info.pCommandBuffers = &command_;
+ if (vkQueueSubmit(queue_, 1, &submit_info, fence_) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkQueueSubmit Fail");
+
+ VkResult r =
+ vkWaitForFences(device_, 1, &fence_, VK_TRUE, 100000000 /* nanosecond */);
+ if (r == VK_TIMEOUT)
+ return Result("Vulkan::Calling vkWaitForFences Timeout");
+ if (r != VK_SUCCESS)
+ return Result("Vulkan::Calling vkWaitForFences Fail");
+
+ if (vkResetCommandBuffer(command_, 0) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkResetCommandBuffer Fail");
+
+ state_ = CommandBufferState::kInitial;
+ return {};
+}
+
+void CommandBuffer::Shutdown() {
+ vkDestroyFence(device_, fence_, nullptr);
+ vkFreeCommandBuffers(device_, pool_, 1, &command_);
+}
+
+} // namespace vulkan
+
+} // namespace amber
diff --git a/src/vulkan/command.h b/src/vulkan/command.h
new file mode 100644
index 0000000..97bd521
--- /dev/null
+++ b/src/vulkan/command.h
@@ -0,0 +1,79 @@
+// Copyright 2018 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_COMMAND_H_
+#define SRC_VULKAN_COMMAND_H_
+
+#include "amber/result.h"
+#include "vulkan/vulkan.h"
+
+namespace amber {
+
+namespace vulkan {
+
+// Command buffer states based on "5.1. Command Buffer Lifecycle" of Vulkan
+// spec
+enum class CommandBufferState : uint8_t {
+ kInitial = 0,
+ kRecording,
+ kExecutable,
+ kPending,
+ kInvalid,
+};
+
+class CommandPool {
+ public:
+ explicit CommandPool(VkDevice device);
+ ~CommandPool();
+
+ Result Initialize(uint32_t queue_family_index);
+ VkCommandPool GetCommandPool() const { return pool_; }
+ void Shutdown();
+
+ private:
+ VkDevice device_ = VK_NULL_HANDLE;
+ VkCommandPool pool_ = VK_NULL_HANDLE;
+};
+
+class CommandBuffer {
+ public:
+ CommandBuffer(VkDevice device, VkCommandPool pool, VkQueue queue);
+ ~CommandBuffer();
+
+ Result Initialize();
+ VkCommandBuffer GetCommandBuffer() const { return command_; }
+ void Shutdown();
+
+ // Do nothing and return if it is already ready to record. If it is in
+ // initial state, call command begin API and make it ready to record.
+ // Otherwise, report error.
+ Result BeginIfNotInRecording();
+
+ Result End();
+ Result SubmitAndReset();
+
+ private:
+ VkDevice device_ = VK_NULL_HANDLE;
+ VkCommandPool pool_ = VK_NULL_HANDLE;
+ VkQueue queue_ = VK_NULL_HANDLE;
+ VkCommandBuffer command_ = VK_NULL_HANDLE;
+ VkFence fence_ = VK_NULL_HANDLE;
+ CommandBufferState state_ = CommandBufferState::kInitial;
+};
+
+} // namespace vulkan
+
+} // namespace amber
+
+#endif // SRC_VULKAN_COMMAND_H_
diff --git a/src/vulkan/descriptor.cc b/src/vulkan/descriptor.cc
new file mode 100644
index 0000000..294e3b6
--- /dev/null
+++ b/src/vulkan/descriptor.cc
@@ -0,0 +1,58 @@
+// Copyright 2018 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>
+
+namespace amber {
+namespace vulkan {
+
+VkDescriptorType ToVkDescriptorType(DescriptorType type) {
+ switch (type) {
+ case DescriptorType::kStorageImage:
+ return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
+ case DescriptorType::kSampler:
+ return VK_DESCRIPTOR_TYPE_SAMPLER;
+ case DescriptorType::kSampledImage:
+ return VK_DESCRIPTOR_TYPE_SAMPLED_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;
+ case DescriptorType::kStorageBuffer:
+ return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ case DescriptorType::kUniformBuffer:
+ return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ case DescriptorType::kDynamicUniformBuffer:
+ return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+ case DescriptorType::kDynamicStorageBuffer:
+ return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
+ case DescriptorType::kInputAttachment:
+ return VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
+ }
+
+ assert(false && "Unknown resource type");
+ return VK_DESCRIPTOR_TYPE_SAMPLER;
+}
+
+Descriptor::Descriptor(DescriptorType type, uint32_t desc_set, uint32_t binding)
+ : type_(type), descriptor_set_(desc_set), binding_(binding) {}
+
+Descriptor::~Descriptor() = default;
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/descriptor.h b/src/vulkan/descriptor.h
new file mode 100644
index 0000000..ea24733
--- /dev/null
+++ b/src/vulkan/descriptor.h
@@ -0,0 +1,92 @@
+// Copyright 2018 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 "amber/result.h"
+#include "vulkan/vulkan.h"
+
+namespace amber {
+namespace vulkan {
+
+enum class DescriptorType : uint8_t {
+ kStorageImage = 0,
+ kSampler,
+ kSampledImage,
+ kCombinedImageSampler,
+ kUniformTexelBuffer,
+ kStorageTexelBuffer,
+ kStorageBuffer,
+ kUniformBuffer,
+ kDynamicUniformBuffer,
+ kDynamicStorageBuffer,
+ kInputAttachment,
+};
+
+VkDescriptorType ToVkDescriptorType(DescriptorType type);
+
+class Descriptor {
+ public:
+ Descriptor(DescriptorType type, uint32_t desc_set, uint32_t binding);
+
+ ~Descriptor();
+
+ uint32_t GetDescriptorSet() const { return descriptor_set_; }
+ uint32_t GetBinding() const { return binding_; }
+ bool operator<(const Descriptor& r) {
+ if (descriptor_set_ == r.descriptor_set_)
+ return binding_ < r.binding_;
+ return descriptor_set_ < r.descriptor_set_;
+ }
+
+ DescriptorType GetType() const { return type_; }
+
+ bool IsStorageImage() const { return type_ == DescriptorType::kStorageImage; }
+ bool IsSampler() const { return type_ == DescriptorType::kSampler; }
+ bool IsSampledImage() const { return type_ == DescriptorType::kSampledImage; }
+ bool IsCombinedImageSampler() const {
+ return type_ == DescriptorType::kCombinedImageSampler;
+ }
+ bool IsUniformTexelBuffer() const {
+ return type_ == DescriptorType::kUniformTexelBuffer;
+ }
+ bool IsStorageTexelBuffer() const {
+ return type_ == DescriptorType::kStorageTexelBuffer;
+ }
+ bool IsStorageBuffer() const {
+ return type_ == DescriptorType::kStorageBuffer;
+ }
+ bool IsUniformBuffer() const {
+ return type_ == DescriptorType::kUniformBuffer;
+ }
+ bool IsDynamicUniformBuffer() const {
+ return type_ == DescriptorType::kDynamicUniformBuffer;
+ }
+ bool IsDynamicStorageBuffer() const {
+ return type_ == DescriptorType::kDynamicStorageBuffer;
+ }
+
+ private:
+ DescriptorType type_ = DescriptorType::kSampledImage;
+ uint32_t descriptor_set_ = 0;
+ uint32_t binding_ = 0;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_DESCRIPTOR_H_
diff --git a/src/vulkan/device.cc b/src/vulkan/device.cc
new file mode 100644
index 0000000..c5dc430
--- /dev/null
+++ b/src/vulkan/device.cc
@@ -0,0 +1,155 @@
+// Copyright 2018 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/device.h"
+
+#include <memory>
+#include <vector>
+
+#include "src/make_unique.h"
+
+namespace amber {
+
+namespace vulkan {
+
+Device::Device() = default;
+Device::Device(VkDevice device) : device_(device), destroy_device_(false) {}
+Device::~Device() = default;
+
+void Device::Shutdown() {
+ if (destroy_device_) {
+ vkDestroyDevice(device_, nullptr);
+ vkDestroyInstance(instance_, nullptr);
+ }
+}
+
+Result Device::Initialize() {
+ if (device_ == VK_NULL_HANDLE) {
+ Result r = CreateInstance();
+ if (!r.IsSuccess())
+ return r;
+
+ r = ChoosePhysicalDevice();
+ if (!r.IsSuccess())
+ return r;
+
+ r = CreateDevice();
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ if (queue_ == VK_NULL_HANDLE) {
+ vkGetDeviceQueue(device_, queue_family_index_, 0, &queue_);
+ if (queue_ == VK_NULL_HANDLE)
+ return Result("Vulkan::Calling vkGetDeviceQueue Fail");
+ }
+ return {};
+}
+
+bool Device::ChooseQueueFamilyIndex(const VkPhysicalDevice& physical_device) {
+ uint32_t count;
+ std::vector<VkQueueFamilyProperties> properties;
+
+ vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, nullptr);
+ properties.resize(count);
+ vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count,
+ properties.data());
+
+ for (uint32_t i = 0; i < count; ++i) {
+ if (properties[i].queueFlags &
+ (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) {
+ queue_family_flags_ = properties[i].queueFlags;
+ queue_family_index_ = i;
+ return true;
+ }
+ }
+
+ for (uint32_t i = 0; i < count; ++i) {
+ if (properties[i].queueFlags & VK_QUEUE_COMPUTE_BIT) {
+ queue_family_flags_ = properties[i].queueFlags;
+ queue_family_index_ = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+Result Device::CreateInstance() {
+ VkApplicationInfo appInfo = {};
+ appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+ appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0);
+
+ VkInstanceCreateInfo instInfo = {};
+ instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ instInfo.pApplicationInfo = &appInfo;
+ // TODO(jaebaek): Enable layers, extensions
+
+ if (vkCreateInstance(&instInfo, nullptr, &instance_) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkCreateInstance Fail");
+
+ return {};
+}
+
+Result Device::ChoosePhysicalDevice() {
+ uint32_t count;
+ std::vector<VkPhysicalDevice> physical_devices;
+
+ if (vkEnumeratePhysicalDevices(instance_, &count, nullptr) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkEnumeratePhysicalDevices Fail");
+ physical_devices.resize(count);
+ if (vkEnumeratePhysicalDevices(instance_, &count, physical_devices.data()) !=
+ VK_SUCCESS)
+ return Result("Vulkan::Calling vkEnumeratePhysicalDevices Fail");
+
+ for (uint32_t i = 0; i < count; ++i) {
+ VkPhysicalDeviceProperties property;
+ vkGetPhysicalDeviceProperties(physical_devices[i], &property);
+ // TODO(jaebaek): Check physical device property
+
+ if (ChooseQueueFamilyIndex(physical_devices[i])) {
+ physical_device_ = physical_devices[i];
+ vkGetPhysicalDeviceMemoryProperties(physical_device_,
+ &physical_memory_properties_);
+ return {};
+ }
+ }
+
+ return Result("Vulkan::No physical device supports Vulkan");
+}
+
+Result Device::CreateDevice() {
+ VkDeviceQueueCreateInfo queue_info;
+ const float priorities[] = {1.0f};
+
+ queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queue_info.queueFamilyIndex = queue_family_index_;
+ queue_info.queueCount = 1;
+ queue_info.pQueuePriorities = priorities;
+
+ VkDeviceCreateInfo info = {};
+ info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+ info.pQueueCreateInfos = &queue_info;
+ info.queueCreateInfoCount = 1;
+ // TODO(jaebaek): Enable layers, extensions, features
+
+ if (vkCreateDevice(physical_device_, &info, nullptr, &device_) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkCreateDevice Fail");
+
+ return {};
+}
+
+} // namespace vulkan
+
+} // namespace amber
diff --git a/src/vulkan/device.h b/src/vulkan/device.h
new file mode 100644
index 0000000..634e07d
--- /dev/null
+++ b/src/vulkan/device.h
@@ -0,0 +1,76 @@
+// Copyright 2018 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_DEVICE_H_
+#define SRC_VULKAN_DEVICE_H_
+
+#include <memory>
+
+#include "amber/result.h"
+#include "vulkan/vulkan.h"
+
+namespace amber {
+
+namespace vulkan {
+
+class Device {
+ public:
+ Device();
+ explicit Device(VkDevice device);
+ ~Device();
+
+ Result Initialize();
+ void Shutdown();
+
+ VkDevice GetDevice() const { return device_; }
+ uint32_t GetQueueFamilyIndex() const { return queue_family_index_; }
+ VkQueue GetQueue() const { return queue_; }
+ const VkPhysicalDeviceMemoryProperties& GetPhysicalMemoryProperties() const {
+ return physical_memory_properties_;
+ }
+
+ private:
+ Result CreateInstance();
+
+ // Get a physical device by checking if the physical device has a proper
+ // queue family.
+ Result ChoosePhysicalDevice();
+
+ // Return true if |physical_device| has a queue family that supports both
+ // graphics and compute or only a compute pipeline. If the proper queue
+ // family exists, |queue_family_index_| and |queue_family_flags_| will have
+ // the queue family index and flags, respectively. Return false if the proper
+ // queue family does not exist.
+ bool ChooseQueueFamilyIndex(const VkPhysicalDevice& physical_device);
+
+ // Create a logical device.
+ Result CreateDevice();
+
+ VkInstance instance_ = VK_NULL_HANDLE;
+ VkPhysicalDevice physical_device_ = VK_NULL_HANDLE;
+ VkPhysicalDeviceMemoryProperties physical_memory_properties_ = {};
+ VkDevice device_ = VK_NULL_HANDLE;
+ VkQueueFlags queue_family_flags_ = 0;
+ uint32_t queue_family_index_ = 0;
+
+ VkQueue queue_ = VK_NULL_HANDLE;
+
+ bool destroy_device_ = true;
+};
+
+} // namespace vulkan
+
+} // namespace amber
+
+#endif // SRC_VULKAN_DEVICE_H_
diff --git a/src/vulkan/engine_vulkan.cc b/src/vulkan/engine_vulkan.cc
new file mode 100644
index 0000000..4618296
--- /dev/null
+++ b/src/vulkan/engine_vulkan.cc
@@ -0,0 +1,278 @@
+// Copyright 2018 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"
+
+#include <algorithm>
+
+#include "src/make_unique.h"
+#include "src/vulkan/format_data.h"
+#include "src/vulkan/graphics_pipeline.h"
+
+namespace amber {
+namespace vulkan {
+namespace {
+
+const uint32_t kFramebufferWidth = 250;
+const uint32_t kFramebufferHeight = 250;
+const VkFormat kDefaultColorFormat = VK_FORMAT_R8G8B8A8_UNORM;
+
+VkShaderStageFlagBits ToVkShaderStage(ShaderType type) {
+ switch (type) {
+ case ShaderType::kGeometry:
+ return VK_SHADER_STAGE_GEOMETRY_BIT;
+ case ShaderType::kFragment:
+ return VK_SHADER_STAGE_FRAGMENT_BIT;
+ case ShaderType::kVertex:
+ return VK_SHADER_STAGE_VERTEX_BIT;
+ case ShaderType::kTessellationControl:
+ return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
+ case ShaderType::kTessellationEvaluation:
+ return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
+ case ShaderType::kCompute:
+ return VK_SHADER_STAGE_COMPUTE_BIT;
+ }
+
+ // Unreachable
+ return VK_SHADER_STAGE_FRAGMENT_BIT;
+}
+
+} // namespace
+
+EngineVulkan::EngineVulkan() : Engine() {}
+
+EngineVulkan::~EngineVulkan() = default;
+
+Result EngineVulkan::InitDeviceAndCreateCommand() {
+ Result r = device_->Initialize();
+ if (!r.IsSuccess())
+ return r;
+
+ if (!pool_) {
+ pool_ = MakeUnique<CommandPool>(device_->GetDevice());
+ r = pool_->Initialize(device_->GetQueueFamilyIndex());
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ return {};
+}
+
+Result EngineVulkan::Initialize() {
+ if (device_)
+ return Result("Vulkan::Set device_ already exists");
+
+ device_ = MakeUnique<Device>();
+ return InitDeviceAndCreateCommand();
+}
+
+Result EngineVulkan::InitializeWithDevice(void* default_device) {
+ if (device_)
+ return Result("Vulkan::Set device_ already exists");
+
+ VkDevice device = static_cast<VkDevice>(default_device);
+ if (device == VK_NULL_HANDLE)
+ return Result("Vulkan::Set VK_NULL_HANDLE is given");
+
+ device_ = MakeUnique<Device>(device);
+ return InitDeviceAndCreateCommand();
+}
+
+Result EngineVulkan::Shutdown() {
+ for (auto it = modules_.begin(); it != modules_.end(); ++it)
+ vkDestroyShaderModule(device_->GetDevice(), it->second, nullptr);
+
+ pipeline_->Shutdown();
+ pool_->Shutdown();
+ device_->Shutdown();
+ return {};
+}
+
+Result EngineVulkan::AddRequirement(Feature feature, const Format* fmt) {
+ auto it = std::find_if(requirements_.begin(), requirements_.end(),
+ [&feature](const EngineVulkan::Requirement& req) {
+ return req.feature == feature;
+ });
+ if (it != requirements_.end())
+ return Result("Vulkan::Feature Already Exists");
+
+ requirements_.push_back({feature, fmt});
+ return {};
+}
+
+Result EngineVulkan::CreatePipeline(PipelineType type) {
+ if (type == PipelineType::kCompute)
+ return Result("Vulkan::Compute Pipeline Not Implemented");
+
+ VkFormat frame_buffer_format = kDefaultColorFormat;
+ auto it_frame_buffer =
+ std::find_if(requirements_.begin(), requirements_.end(),
+ [](const EngineVulkan::Requirement& req) {
+ return req.feature == Feature::kFramebuffer;
+ });
+ if (it_frame_buffer != requirements_.end()) {
+ frame_buffer_format = ToVkFormat(it_frame_buffer->format->GetFormatType());
+ }
+
+ VkFormat depth_stencil_format = VK_FORMAT_UNDEFINED;
+ auto it_depth_stencil =
+ std::find_if(requirements_.begin(), requirements_.end(),
+ [](const EngineVulkan::Requirement& req) {
+ return req.feature == Feature::kDepthStencil;
+ });
+ if (it_depth_stencil != requirements_.end()) {
+ depth_stencil_format =
+ ToVkFormat(it_depth_stencil->format->GetFormatType());
+ }
+
+ pipeline_ = MakeUnique<GraphicsPipeline>(
+ type, device_->GetDevice(), device_->GetPhysicalMemoryProperties(),
+ frame_buffer_format, depth_stencil_format, GetShaderStageInfo());
+
+ return pipeline_->AsGraphics()->Initialize(
+ kFramebufferWidth, kFramebufferHeight, pool_->GetCommandPool(),
+ device_->GetQueue());
+}
+
+Result EngineVulkan::SetShader(ShaderType type,
+ const std::vector<uint32_t>& data) {
+ if (type == ShaderType::kCompute)
+ return Result("Vulkan::Compute Pipeline Not Implemented");
+
+ VkShaderModuleCreateInfo info = {};
+ info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ info.codeSize = data.size() * sizeof(uint32_t);
+ info.pCode = data.data();
+
+ auto it = modules_.find(type);
+ if (it != modules_.end())
+ return Result("Vulkan::Setting Duplicated Shader Types Fail");
+
+ VkShaderModule shader;
+ if (vkCreateShaderModule(device_->GetDevice(), &info, nullptr, &shader) !=
+ VK_SUCCESS) {
+ return Result("Vulkan::Calling vkCreateShaderModule Fail");
+ }
+
+ modules_[type] = shader;
+ return {};
+}
+
+std::vector<VkPipelineShaderStageCreateInfo>
+EngineVulkan::GetShaderStageInfo() {
+ std::vector<VkPipelineShaderStageCreateInfo> stage_info(modules_.size());
+ uint32_t stage_count = 0;
+ for (auto it : modules_) {
+ stage_info[stage_count] = {};
+ stage_info[stage_count].sType =
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ stage_info[stage_count].stage = ToVkShaderStage(it.first);
+ stage_info[stage_count].module = it.second;
+ // TODO(jaebaek): Handle entry point command
+ stage_info[stage_count].pName = "main";
+ ++stage_count;
+ }
+ return stage_info;
+}
+
+Result EngineVulkan::SetBuffer(BufferType type,
+ uint8_t location,
+ const Format& format,
+ const std::vector<Value>& values) {
+ if (!pipeline_)
+ return Result("Vulkan::SetBuffer no Pipeline exists");
+
+ // TODO(jaebaek): Doublecheck those buffers are only for the graphics
+ // pipeline.
+ if (!pipeline_->IsGraphics())
+ return Result("Vulkan::SetBuffer for Non-Graphics Pipeline");
+
+ pipeline_->AsGraphics()->SetBuffer(type, location, format, values);
+ return {};
+}
+
+Result EngineVulkan::ExecuteClearColor(const ClearColorCommand* command) {
+ if (!pipeline_->IsGraphics())
+ return Result("Vulkan::Clear Color Command for Non-Graphics Pipeline");
+
+ return pipeline_->AsGraphics()->SetClearColor(
+ command->GetR(), command->GetG(), command->GetB(), command->GetA());
+}
+
+Result EngineVulkan::ExecuteClearStencil(const ClearStencilCommand* command) {
+ if (!pipeline_->IsGraphics())
+ return Result("Vulkan::Clear Stencil Command for Non-Graphics Pipeline");
+
+ return pipeline_->AsGraphics()->SetClearStencil(command->GetValue());
+}
+
+Result EngineVulkan::ExecuteClearDepth(const ClearDepthCommand* command) {
+ if (!pipeline_->IsGraphics())
+ return Result("Vulkan::Clear Depth Command for Non-Graphics Pipeline");
+
+ return pipeline_->AsGraphics()->SetClearDepth(command->GetValue());
+}
+
+Result EngineVulkan::ExecuteClear(const ClearCommand*) {
+ if (!pipeline_->IsGraphics())
+ return Result("Vulkan::Clear Command for Non-Graphics Pipeline");
+
+ return pipeline_->AsGraphics()->Clear();
+}
+
+Result EngineVulkan::ExecuteDrawRect(const DrawRectCommand*) {
+ return Result("Vulkan::ExecuteDrawRect Not Implemented");
+}
+
+Result EngineVulkan::ExecuteDrawArrays(const DrawArraysCommand*) {
+ if (!pipeline_->IsGraphics())
+ return Result("Vulkan::DrawArrays for Non-Graphics Pipeline");
+
+ return pipeline_->AsGraphics()->Draw();
+}
+
+Result EngineVulkan::ExecuteCompute(const ComputeCommand*) {
+ return Result("Vulkan::ExecuteCompute Not Implemented");
+}
+
+Result EngineVulkan::ExecuteEntryPoint(const EntryPointCommand*) {
+ return Result("Vulkan::ExecuteEntryPoint Not Implemented");
+}
+
+Result EngineVulkan::ExecutePatchParameterVertices(
+ const PatchParameterVerticesCommand*) {
+ return Result("Vulkan::ExecutePatch Not Implemented");
+}
+
+Result EngineVulkan::ExecuteProbe(const ProbeCommand* command) {
+ if (!pipeline_->IsGraphics())
+ return Result("Vulkan::Probe FrameBuffer for Non-Graphics Pipeline");
+
+ return pipeline_->AsGraphics()->Probe(command);
+}
+
+Result EngineVulkan::ExecuteProbeSSBO(const ProbeSSBOCommand*) {
+ return Result("Vulkan::ExecuteProbeSSBO Not Implemented");
+}
+
+Result EngineVulkan::ExecuteBuffer(const BufferCommand*) {
+ return Result("Vulkan::ExecuteBuffer Not Implemented");
+}
+
+Result EngineVulkan::ExecuteTolerance(const ToleranceCommand*) {
+ return Result("Vulkan::ExecuteTolerance Not Implemented");
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/engine_vulkan.h b/src/vulkan/engine_vulkan.h
new file mode 100644
index 0000000..c8c2a00
--- /dev/null
+++ b/src/vulkan/engine_vulkan.h
@@ -0,0 +1,85 @@
+// Copyright 2018 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_ENGINE_VULKAN_H_
+#define SRC_VULKAN_ENGINE_VULKAN_H_
+
+#include <memory>
+#include <unordered_map>
+
+#include "src/cast_hash.h"
+#include "src/engine.h"
+#include "src/vulkan/command.h"
+#include "src/vulkan/device.h"
+#include "src/vulkan/pipeline.h"
+#include "vulkan/vulkan.h"
+
+namespace amber {
+namespace vulkan {
+
+class EngineVulkan : public Engine {
+ public:
+ EngineVulkan();
+ ~EngineVulkan() override;
+
+ // Engine
+ Result Initialize() override;
+ Result InitializeWithDevice(void* default_device) override;
+ Result Shutdown() override;
+ Result AddRequirement(Feature feature, const Format*) override;
+ Result CreatePipeline(PipelineType type) override;
+ Result SetShader(ShaderType type, const std::vector<uint32_t>& data) override;
+ Result SetBuffer(BufferType type,
+ uint8_t location,
+ const Format& format,
+ const std::vector<Value>& data) override;
+ Result ExecuteClearColor(const ClearColorCommand* cmd) override;
+ Result ExecuteClearStencil(const ClearStencilCommand* cmd) override;
+ Result ExecuteClearDepth(const ClearDepthCommand* cmd) override;
+ Result ExecuteClear(const ClearCommand* cmd) override;
+ Result ExecuteDrawRect(const DrawRectCommand* cmd) override;
+ Result ExecuteDrawArrays(const DrawArraysCommand* cmd) override;
+ Result ExecuteCompute(const ComputeCommand* cmd) override;
+ Result ExecuteEntryPoint(const EntryPointCommand* cmd) override;
+ Result ExecutePatchParameterVertices(
+ const PatchParameterVerticesCommand* cmd) override;
+ Result ExecuteProbe(const ProbeCommand* cmd) override;
+ Result ExecuteProbeSSBO(const ProbeSSBOCommand* cmd) override;
+ Result ExecuteBuffer(const BufferCommand* cmd) override;
+ Result ExecuteTolerance(const ToleranceCommand* cmd) override;
+
+ private:
+ Result InitDeviceAndCreateCommand();
+
+ std::vector<VkPipelineShaderStageCreateInfo> GetShaderStageInfo();
+
+ std::unique_ptr<Device> device_;
+ std::unique_ptr<CommandPool> pool_;
+ std::unique_ptr<Pipeline> pipeline_;
+
+ std::unordered_map<ShaderType, VkShaderModule, CastHash<ShaderType>> modules_;
+
+ struct Requirement {
+ Feature feature;
+ const Format* format;
+ };
+
+ std::vector<Requirement> requirements_;
+ std::vector<Requirement>::iterator FindFeature(Feature feature);
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_ENGINE_VULKAN_H_
diff --git a/src/vulkan/find_vulkan.cmake b/src/vulkan/find_vulkan.cmake
new file mode 100644
index 0000000..630346a
--- /dev/null
+++ b/src/vulkan/find_vulkan.cmake
@@ -0,0 +1,77 @@
+# Copyright 2018 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 this file to find Vulkan and and set up compilation and linking.
+
+
+# Export these settings to the includer.
+set(Vulkan_FOUND FALSE)
+set(VULKAN_LIB "")
+
+
+# Our first choice is to pick up the Vulkan headers from an enclosing project.
+# And if that's the case, then use Vulkan libraries as specified by
+# Vulkan_LIBRARIES, with a default library of "vulkan".
+set(X "${Vulkan-Headers_SOURCE_DIR}/include")
+if (IS_DIRECTORY "${X}")
+ 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)
+
+ if (${HAVE_VULKAN_HEADER})
+ if ("${Vulkan_LIBRARIES}" STREQUAL "")
+ message(STATUS "Amber: Defaulting to Vulkan library: vulkan")
+ set(VULKAN_LIB vulkan)
+ else()
+ message(STATUS "Amber: Using specified Vulkan libraries: ${Vulkan_LIBRARIES}")
+ set(VULKAN_LIB "${Vulkan_LIBRARIES}")
+ endif()
+ # For now assume we have Vulkan. We have its header, but we haven't checked
+ # for the library.
+ # TODO(dneto): Actually check for the libraries.
+ set(Vulkan_FOUND TRUE)
+ endif()
+endif()
+unset(X)
+
+if (NOT ${Vulkan_FOUND})
+ # If we aren't already building a Vulkan library, then use CMake to find it.
+ if(NOT ${CMAKE_VERSION} VERSION_LESS "3.7")
+ # LunarG added FindVulkan support to CMake 3.7. If you have the Vulkan SDK
+ # published by LunarG, then set environment variables:
+ # VULKAN_SDK should point to the platform-specific SDK directory containing
+ # the include and lib directories.
+ # VK_ICD_FILENAMES should point to ICD JSON file.
+
+ # Example, with the LunarG SDK macOS edition with MoltenVK:
+ # export VULKAN_SDK="$HOME/vulkan-macos-1.1.85.0/macOS"
+ # export VK_ICD_FILENAMES="$VULKAN_SDK/etc/vulkan/icd/MoltenVK_icd.json"
+ # See https://cmake.org/cmake/help/v3.7/module/FindVulkan.html
+ find_package(Vulkan)
+ if(${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}")
+ endif()
+ endif()
+endif()
+
+if (NOT ${Vulkan_FOUND})
+ message(STATUS "Amber: Did not find Vulkan")
+endif()
diff --git a/src/vulkan/format_data.cc b/src/vulkan/format_data.cc
new file mode 100644
index 0000000..a78aad5
--- /dev/null
+++ b/src/vulkan/format_data.cc
@@ -0,0 +1,299 @@
+// Copyright 2018 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/format_data.h"
+
+namespace amber {
+namespace vulkan {
+
+VkFormat ToVkFormat(FormatType type) {
+ switch (type) {
+ case FormatType::kUnknown:
+ return VK_FORMAT_UNDEFINED;
+ case FormatType::kA1R5G5B5_UNORM_PACK16:
+ return VK_FORMAT_A1R5G5B5_UNORM_PACK16;
+ case FormatType::kA2B10G10R10_SINT_PACK32:
+ return VK_FORMAT_A2B10G10R10_SINT_PACK32;
+ case FormatType::kA2B10G10R10_SNORM_PACK32:
+ return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
+ case FormatType::kA2B10G10R10_SSCALED_PACK32:
+ return VK_FORMAT_A2B10G10R10_SSCALED_PACK32;
+ case FormatType::kA2B10G10R10_UINT_PACK32:
+ return VK_FORMAT_A2B10G10R10_UINT_PACK32;
+ case FormatType::kA2B10G10R10_UNORM_PACK32:
+ return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
+ case FormatType::kA2B10G10R10_USCALED_PACK32:
+ return VK_FORMAT_A2B10G10R10_USCALED_PACK32;
+ case FormatType::kA2R10G10B10_SINT_PACK32:
+ return VK_FORMAT_A2R10G10B10_SINT_PACK32;
+ case FormatType::kA2R10G10B10_SNORM_PACK32:
+ return VK_FORMAT_A2R10G10B10_SNORM_PACK32;
+ case FormatType::kA2R10G10B10_SSCALED_PACK32:
+ return VK_FORMAT_A2R10G10B10_SSCALED_PACK32;
+ case FormatType::kA2R10G10B10_UINT_PACK32:
+ return VK_FORMAT_A2R10G10B10_UINT_PACK32;
+ case FormatType::kA2R10G10B10_UNORM_PACK32:
+ return VK_FORMAT_A2R10G10B10_UNORM_PACK32;
+ case FormatType::kA2R10G10B10_USCALED_PACK32:
+ return VK_FORMAT_A2R10G10B10_USCALED_PACK32;
+ case FormatType::kA8B8G8R8_SINT_PACK32:
+ return VK_FORMAT_A8B8G8R8_SINT_PACK32;
+ case FormatType::kA8B8G8R8_SNORM_PACK32:
+ return VK_FORMAT_A8B8G8R8_SNORM_PACK32;
+ case FormatType::kA8B8G8R8_SRGB_PACK32:
+ return VK_FORMAT_A8B8G8R8_SRGB_PACK32;
+ case FormatType::kA8B8G8R8_SSCALED_PACK32:
+ return VK_FORMAT_A8B8G8R8_SSCALED_PACK32;
+ case FormatType::kA8B8G8R8_UINT_PACK32:
+ return VK_FORMAT_A8B8G8R8_UINT_PACK32;
+ case FormatType::kA8B8G8R8_UNORM_PACK32:
+ return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
+ case FormatType::kA8B8G8R8_USCALED_PACK32:
+ return VK_FORMAT_A8B8G8R8_USCALED_PACK32;
+ case FormatType::kB10G11R11_UFLOAT_PACK32:
+ return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
+ case FormatType::kB4G4R4A4_UNORM_PACK16:
+ return VK_FORMAT_B4G4R4A4_UNORM_PACK16;
+ case FormatType::kB5G5R5A1_UNORM_PACK16:
+ return VK_FORMAT_B5G5R5A1_UNORM_PACK16;
+ case FormatType::kB5G6R5_UNORM_PACK16:
+ return VK_FORMAT_B5G6R5_UNORM_PACK16;
+ case FormatType::kB8G8R8A8_SINT:
+ return VK_FORMAT_B8G8R8A8_SINT;
+ case FormatType::kB8G8R8A8_SNORM:
+ return VK_FORMAT_B8G8R8A8_SNORM;
+ case FormatType::kB8G8R8A8_SRGB:
+ return VK_FORMAT_B8G8R8A8_SRGB;
+ case FormatType::kB8G8R8A8_SSCALED:
+ return VK_FORMAT_B8G8R8A8_SSCALED;
+ case FormatType::kB8G8R8A8_UINT:
+ return VK_FORMAT_B8G8R8A8_UINT;
+ case FormatType::kB8G8R8A8_UNORM:
+ return VK_FORMAT_B8G8R8A8_UNORM;
+ case FormatType::kB8G8R8A8_USCALED:
+ return VK_FORMAT_B8G8R8A8_USCALED;
+ case FormatType::kB8G8R8_SINT:
+ return VK_FORMAT_B8G8R8_SINT;
+ case FormatType::kB8G8R8_SNORM:
+ return VK_FORMAT_B8G8R8_SNORM;
+ case FormatType::kB8G8R8_SRGB:
+ return VK_FORMAT_B8G8R8_SRGB;
+ case FormatType::kB8G8R8_SSCALED:
+ return VK_FORMAT_B8G8R8_SSCALED;
+ case FormatType::kB8G8R8_UINT:
+ return VK_FORMAT_B8G8R8_UINT;
+ case FormatType::kB8G8R8_UNORM:
+ return VK_FORMAT_B8G8R8_UNORM;
+ case FormatType::kB8G8R8_USCALED:
+ return VK_FORMAT_B8G8R8_USCALED;
+ case FormatType::kD16_UNORM:
+ return VK_FORMAT_D16_UNORM;
+ case FormatType::kD16_UNORM_S8_UINT:
+ return VK_FORMAT_D16_UNORM_S8_UINT;
+ case FormatType::kD24_UNORM_S8_UINT:
+ return VK_FORMAT_D24_UNORM_S8_UINT;
+ case FormatType::kD32_SFLOAT:
+ return VK_FORMAT_D32_SFLOAT;
+ case FormatType::kD32_SFLOAT_S8_UINT:
+ return VK_FORMAT_D32_SFLOAT_S8_UINT;
+ case FormatType::kR16G16B16A16_SFLOAT:
+ return VK_FORMAT_R16G16B16A16_SFLOAT;
+ case FormatType::kR16G16B16A16_SINT:
+ return VK_FORMAT_R16G16B16A16_SINT;
+ case FormatType::kR16G16B16A16_SNORM:
+ return VK_FORMAT_R16G16B16A16_SNORM;
+ case FormatType::kR16G16B16A16_SSCALED:
+ return VK_FORMAT_R16G16B16A16_SSCALED;
+ case FormatType::kR16G16B16A16_UINT:
+ return VK_FORMAT_R16G16B16A16_UINT;
+ case FormatType::kR16G16B16A16_UNORM:
+ return VK_FORMAT_R16G16B16A16_UNORM;
+ case FormatType::kR16G16B16A16_USCALED:
+ return VK_FORMAT_R16G16B16A16_USCALED;
+ case FormatType::kR16G16B16_SFLOAT:
+ return VK_FORMAT_R16G16B16_SFLOAT;
+ case FormatType::kR16G16B16_SINT:
+ return VK_FORMAT_R16G16B16_SINT;
+ case FormatType::kR16G16B16_SNORM:
+ return VK_FORMAT_R16G16B16_SNORM;
+ case FormatType::kR16G16B16_SSCALED:
+ return VK_FORMAT_R16G16B16_SSCALED;
+ case FormatType::kR16G16B16_UINT:
+ return VK_FORMAT_R16G16B16_UINT;
+ case FormatType::kR16G16B16_UNORM:
+ return VK_FORMAT_R16G16B16_UNORM;
+ case FormatType::kR16G16B16_USCALED:
+ return VK_FORMAT_R16G16B16_USCALED;
+ case FormatType::kR16G16_SFLOAT:
+ return VK_FORMAT_R16G16_SFLOAT;
+ case FormatType::kR16G16_SINT:
+ return VK_FORMAT_R16G16_SINT;
+ case FormatType::kR16G16_SNORM:
+ return VK_FORMAT_R16G16_SNORM;
+ case FormatType::kR16G16_SSCALED:
+ return VK_FORMAT_R16G16_SSCALED;
+ case FormatType::kR16G16_UINT:
+ return VK_FORMAT_R16G16_UINT;
+ case FormatType::kR16G16_UNORM:
+ return VK_FORMAT_R16G16_UNORM;
+ case FormatType::kR16G16_USCALED:
+ return VK_FORMAT_R16G16_USCALED;
+ case FormatType::kR16_SFLOAT:
+ return VK_FORMAT_R16_SFLOAT;
+ case FormatType::kR16_SINT:
+ return VK_FORMAT_R16_SINT;
+ case FormatType::kR16_SNORM:
+ return VK_FORMAT_R16_SNORM;
+ case FormatType::kR16_SSCALED:
+ return VK_FORMAT_R16_SSCALED;
+ case FormatType::kR16_UINT:
+ return VK_FORMAT_R16_UINT;
+ case FormatType::kR16_UNORM:
+ return VK_FORMAT_R16_UNORM;
+ case FormatType::kR16_USCALED:
+ return VK_FORMAT_R16_USCALED;
+ case FormatType::kR32G32B32A32_SFLOAT:
+ return VK_FORMAT_R32G32B32A32_SFLOAT;
+ case FormatType::kR32G32B32A32_SINT:
+ return VK_FORMAT_R32G32B32A32_SINT;
+ case FormatType::kR32G32B32A32_UINT:
+ return VK_FORMAT_R32G32B32A32_UINT;
+ case FormatType::kR32G32B32_SFLOAT:
+ return VK_FORMAT_R32G32B32_SFLOAT;
+ case FormatType::kR32G32B32_SINT:
+ return VK_FORMAT_R32G32B32_SINT;
+ case FormatType::kR32G32B32_UINT:
+ return VK_FORMAT_R32G32B32_UINT;
+ case FormatType::kR32G32_SFLOAT:
+ return VK_FORMAT_R32G32_SFLOAT;
+ case FormatType::kR32G32_SINT:
+ return VK_FORMAT_R32G32_SINT;
+ case FormatType::kR32G32_UINT:
+ return VK_FORMAT_R32G32_UINT;
+ case FormatType::kR32_SFLOAT:
+ return VK_FORMAT_R32_SFLOAT;
+ case FormatType::kR32_SINT:
+ return VK_FORMAT_R32_SINT;
+ case FormatType::kR32_UINT:
+ return VK_FORMAT_R32_UINT;
+ case FormatType::kR4G4B4A4_UNORM_PACK16:
+ return VK_FORMAT_R4G4B4A4_UNORM_PACK16;
+ case FormatType::kR4G4_UNORM_PACK8:
+ return VK_FORMAT_R4G4_UNORM_PACK8;
+ case FormatType::kR5G5B5A1_UNORM_PACK16:
+ return VK_FORMAT_R5G5B5A1_UNORM_PACK16;
+ case FormatType::kR5G6B5_UNORM_PACK16:
+ return VK_FORMAT_R5G6B5_UNORM_PACK16;
+ case FormatType::kR64G64B64A64_SFLOAT:
+ return VK_FORMAT_R64G64B64A64_SFLOAT;
+ case FormatType::kR64G64B64A64_SINT:
+ return VK_FORMAT_R64G64B64A64_SINT;
+ case FormatType::kR64G64B64A64_UINT:
+ return VK_FORMAT_R64G64B64A64_UINT;
+ case FormatType::kR64G64B64_SFLOAT:
+ return VK_FORMAT_R64G64B64_SFLOAT;
+ case FormatType::kR64G64B64_SINT:
+ return VK_FORMAT_R64G64B64_SINT;
+ case FormatType::kR64G64B64_UINT:
+ return VK_FORMAT_R64G64B64_UINT;
+ case FormatType::kR64G64_SFLOAT:
+ return VK_FORMAT_R64G64_SFLOAT;
+ case FormatType::kR64G64_SINT:
+ return VK_FORMAT_R64G64_SINT;
+ case FormatType::kR64G64_UINT:
+ return VK_FORMAT_R64G64_UINT;
+ case FormatType::kR64_SFLOAT:
+ return VK_FORMAT_R64_SFLOAT;
+ case FormatType::kR64_SINT:
+ return VK_FORMAT_R64_SINT;
+ case FormatType::kR64_UINT:
+ return VK_FORMAT_R64_UINT;
+ case FormatType::kR8G8B8A8_SINT:
+ return VK_FORMAT_R8G8B8A8_SINT;
+ case FormatType::kR8G8B8A8_SNORM:
+ return VK_FORMAT_R8G8B8A8_SNORM;
+ case FormatType::kR8G8B8A8_SRGB:
+ return VK_FORMAT_R8G8B8A8_SRGB;
+ case FormatType::kR8G8B8A8_SSCALED:
+ return VK_FORMAT_R8G8B8A8_SSCALED;
+ case FormatType::kR8G8B8A8_UINT:
+ return VK_FORMAT_R8G8B8A8_UINT;
+ case FormatType::kR8G8B8A8_UNORM:
+ return VK_FORMAT_R8G8B8A8_UNORM;
+ case FormatType::kR8G8B8A8_USCALED:
+ return VK_FORMAT_R8G8B8A8_USCALED;
+ case FormatType::kR8G8B8_SINT:
+ return VK_FORMAT_R8G8B8_SINT;
+ case FormatType::kR8G8B8_SNORM:
+ return VK_FORMAT_R8G8B8_SNORM;
+ case FormatType::kR8G8B8_SRGB:
+ return VK_FORMAT_R8G8B8_SRGB;
+ case FormatType::kR8G8B8_SSCALED:
+ return VK_FORMAT_R8G8B8_SSCALED;
+ case FormatType::kR8G8B8_UINT:
+ return VK_FORMAT_R8G8B8_UINT;
+ case FormatType::kR8G8B8_UNORM:
+ return VK_FORMAT_R8G8B8_UNORM;
+ case FormatType::kR8G8B8_USCALED:
+ return VK_FORMAT_R8G8B8_USCALED;
+ case FormatType::kR8G8_SINT:
+ return VK_FORMAT_R8G8_SINT;
+ case FormatType::kR8G8_SNORM:
+ return VK_FORMAT_R8G8_SNORM;
+ case FormatType::kR8G8_SRGB:
+ return VK_FORMAT_R8G8_SRGB;
+ case FormatType::kR8G8_SSCALED:
+ return VK_FORMAT_R8G8_SSCALED;
+ case FormatType::kR8G8_UINT:
+ return VK_FORMAT_R8G8_UINT;
+ case FormatType::kR8G8_UNORM:
+ return VK_FORMAT_R8G8_UNORM;
+ case FormatType::kR8G8_USCALED:
+ return VK_FORMAT_R8G8_USCALED;
+ case FormatType::kR8_SINT:
+ return VK_FORMAT_R8_SINT;
+ case FormatType::kR8_SNORM:
+ return VK_FORMAT_R8_SNORM;
+ case FormatType::kR8_SRGB:
+ return VK_FORMAT_R8_SRGB;
+ case FormatType::kR8_SSCALED:
+ return VK_FORMAT_R8_SSCALED;
+ case FormatType::kR8_UINT:
+ return VK_FORMAT_R8_UINT;
+ case FormatType::kR8_UNORM:
+ return VK_FORMAT_R8_UNORM;
+ case FormatType::kR8_USCALED:
+ return VK_FORMAT_R8_USCALED;
+ case FormatType::kS8_UINT:
+ return VK_FORMAT_S8_UINT;
+ case FormatType::kX8_D24_UNORM_PACK32:
+ return VK_FORMAT_X8_D24_UNORM_PACK32;
+ }
+ return VK_FORMAT_UNDEFINED;
+}
+
+uint32_t VkFormatToByteSize(VkFormat format) {
+ switch (format) {
+ case VK_FORMAT_R8G8B8A8_UNORM:
+ return 4;
+
+ // TODO(jaebaek): Handle all cases.
+ default:
+ break;
+ }
+ return 0;
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/format_data.h b/src/vulkan/format_data.h
new file mode 100644
index 0000000..c9dc44a
--- /dev/null
+++ b/src/vulkan/format_data.h
@@ -0,0 +1,30 @@
+// Copyright 2018 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_FORMAT_DATA_H_
+#define SRC_VULKAN_FORMAT_DATA_H_
+
+#include "src/format_data.h"
+#include "vulkan/vulkan.h"
+
+namespace amber {
+namespace vulkan {
+
+VkFormat ToVkFormat(FormatType type);
+uint32_t VkFormatToByteSize(VkFormat format);
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_FORMAT_DATA_H_
diff --git a/src/vulkan/frame_buffer.cc b/src/vulkan/frame_buffer.cc
new file mode 100644
index 0000000..ab36bbc
--- /dev/null
+++ b/src/vulkan/frame_buffer.cc
@@ -0,0 +1,83 @@
+// Copyright 2018 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/frame_buffer.h"
+
+#include <limits>
+
+#include "src/make_unique.h"
+
+namespace amber {
+namespace vulkan {
+
+FrameBuffer::FrameBuffer(VkDevice device, uint32_t width, uint32_t height)
+ : device_(device), width_(width), height_(height) {}
+
+FrameBuffer::~FrameBuffer() = default;
+
+Result FrameBuffer::Initialize(
+ VkRenderPass render_pass,
+ VkFormat color_format,
+ VkFormat depth_format,
+ const VkPhysicalDeviceMemoryProperties& properties) {
+ std::vector<VkImageView> attachments;
+
+ if (color_format != VK_FORMAT_UNDEFINED) {
+ color_image_ = MakeUnique<Image>(device_, color_format, width_, height_,
+ depth_, properties);
+ Result r = color_image_->Initialize(VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
+ if (!r.IsSuccess())
+ return r;
+ attachments.push_back(color_image_->GetVkImageView());
+ }
+
+ if (depth_format != VK_FORMAT_UNDEFINED) {
+ depth_image_ = MakeUnique<Image>(device_, depth_format, width_, height_,
+ depth_, properties);
+ Result r =
+ depth_image_->Initialize(VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+ VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
+ if (!r.IsSuccess())
+ return r;
+ attachments.push_back(depth_image_->GetVkImageView());
+ }
+
+ VkFramebufferCreateInfo frame_buffer_info = {};
+ frame_buffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ frame_buffer_info.renderPass = render_pass;
+ frame_buffer_info.attachmentCount = static_cast<uint32_t>(attachments.size());
+ frame_buffer_info.pAttachments = attachments.data();
+ frame_buffer_info.width = width_;
+ frame_buffer_info.height = height_;
+ frame_buffer_info.layers = 1;
+
+ if (vkCreateFramebuffer(device_, &frame_buffer_info, nullptr, &frame_) !=
+ VK_SUCCESS) {
+ return Result("Vulkan::Calling vkCreateFramebuffer Fail");
+ }
+
+ return {};
+}
+
+void FrameBuffer::Shutdown() {
+ vkDestroyFramebuffer(device_, frame_, nullptr);
+ if (color_image_)
+ color_image_->Shutdown();
+ if (depth_image_)
+ depth_image_->Shutdown();
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/frame_buffer.h b/src/vulkan/frame_buffer.h
new file mode 100644
index 0000000..0adf7ca
--- /dev/null
+++ b/src/vulkan/frame_buffer.h
@@ -0,0 +1,59 @@
+// Copyright 2018 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_FRAME_BUFFER_H_
+#define SRC_VULKAN_FRAME_BUFFER_H_
+
+#include "src/vulkan/image.h"
+
+namespace amber {
+namespace vulkan {
+
+class FrameBuffer {
+ public:
+ FrameBuffer(VkDevice device, uint32_t width, uint32_t height);
+ ~FrameBuffer();
+
+ Result Initialize(VkRenderPass render_pass,
+ VkFormat color_format,
+ VkFormat depth_format,
+ const VkPhysicalDeviceMemoryProperties& properties);
+ void Shutdown();
+
+ VkFramebuffer GetFrameBuffer() const { return frame_; }
+ const void* GetColorBufferPtr() const {
+ return color_image_->HostAccessibleMemoryPtr();
+ }
+ VkImage GetColorImage() const { return color_image_->GetVkImage(); }
+ Result CopyColorImageToHost(VkCommandBuffer command) {
+ return color_image_->CopyToHost(command);
+ }
+
+ uint32_t GetWidth() const { return width_; }
+ uint32_t GetHeight() const { return height_; }
+
+ private:
+ VkDevice device_ = VK_NULL_HANDLE;
+ VkFramebuffer frame_ = VK_NULL_HANDLE;
+ std::unique_ptr<Image> color_image_;
+ std::unique_ptr<Image> depth_image_;
+ uint32_t width_ = 0;
+ uint32_t height_ = 0;
+ uint32_t depth_ = 1;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_FRAME_BUFFER_H_
diff --git a/src/vulkan/graphics_pipeline.cc b/src/vulkan/graphics_pipeline.cc
new file mode 100644
index 0000000..500d423
--- /dev/null
+++ b/src/vulkan/graphics_pipeline.cc
@@ -0,0 +1,590 @@
+// Copyright 2018 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/graphics_pipeline.h"
+
+#include <cmath>
+
+#include "src/command.h"
+#include "src/make_unique.h"
+#include "src/vulkan/format_data.h"
+
+namespace amber {
+namespace vulkan {
+namespace {
+
+const VkAttachmentDescription kDefaultAttachmentDesc = {
+ 0, /* flags */
+ VK_FORMAT_UNDEFINED, /* format */
+ VK_SAMPLE_COUNT_1_BIT, /* samples */
+ // TODO(jaebaek): Set up proper loadOp, StoreOp.
+ VK_ATTACHMENT_LOAD_OP_LOAD, /* loadOp */
+ VK_ATTACHMENT_STORE_OP_STORE, /* storeOp */
+ VK_ATTACHMENT_LOAD_OP_LOAD, /* stencilLoadOp */
+ VK_ATTACHMENT_STORE_OP_STORE, /* stencilStoreOp */
+ VK_IMAGE_LAYOUT_UNDEFINED, /* initialLayout */
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, /* finalLayout */
+};
+
+const VkPipelineRasterizationStateCreateInfo kDefaultRasterizationInfo = {
+ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, /* sType */
+ nullptr, /* pNext */
+ 0, /* flags */
+ VK_FALSE, /* depthClampEnable */
+ VK_FALSE, /* rasterizerDiscardEnable */
+ VK_POLYGON_MODE_FILL, /* polygonMode */
+ VK_CULL_MODE_NONE, /* cullMode */
+ VK_FRONT_FACE_CLOCKWISE, /* frontFace */
+ VK_FALSE, /* depthBiasEnable */
+ 0, /* depthBiasConstantFactor */
+ 0, /* depthBiasClamp */
+ 0, /* depthBiasSlopeFactor */
+ 0, /* lineWidth */
+};
+
+const VkSampleMask kSampleMask = ~0U;
+
+const VkPipelineMultisampleStateCreateInfo kMultisampleInfo = {
+ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, /* sType */
+ nullptr, /* pNext */
+ 0, /* flags */
+ kDefaultAttachmentDesc.samples, /* rasterizationSamples */
+ VK_FALSE, /* sampleShadingEnable */
+ 0, /* minSampleShading */
+ &kSampleMask, /* pSampleMask */
+ VK_FALSE, /* alphaToCoverageEnable */
+ VK_FALSE, /* alphaToOneEnable */
+};
+
+const float kEpsilon = 0.002f;
+
+bool IsFloatPixelEqualInt(float pixel, uint8_t expected) {
+ // TODO(jaebaek): Change kEpsilon to tolerance.
+ return std::fabs(pixel - static_cast<float>(expected) / 255.0f) < kEpsilon;
+}
+
+} // namespace
+
+GraphicsPipeline::GraphicsPipeline(
+ PipelineType type,
+ VkDevice device,
+ const VkPhysicalDeviceMemoryProperties& properties,
+ VkFormat color_format,
+ VkFormat depth_stencil_format,
+ std::vector<VkPipelineShaderStageCreateInfo> shader_stage_info)
+ : Pipeline(type, device, properties),
+ color_format_(color_format),
+ depth_stencil_format_(depth_stencil_format),
+ shader_stage_info_(shader_stage_info) {}
+
+GraphicsPipeline::~GraphicsPipeline() = default;
+
+Result GraphicsPipeline::CreateRenderPass() {
+ VkSubpassDescription subpass_desc = {};
+ subpass_desc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+
+ std::vector<VkAttachmentDescription> attachment_desc;
+
+ VkAttachmentReference color_refer = {};
+ VkAttachmentReference depth_refer = {};
+
+ if (color_format_ != VK_FORMAT_UNDEFINED) {
+ attachment_desc.push_back(kDefaultAttachmentDesc);
+ attachment_desc.back().format = color_format_;
+
+ color_refer.attachment = static_cast<uint32_t>(attachment_desc.size() - 1);
+ color_refer.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+ subpass_desc.colorAttachmentCount = 1;
+ subpass_desc.pColorAttachments = &color_refer;
+ }
+
+ if (depth_stencil_format_ != VK_FORMAT_UNDEFINED) {
+ attachment_desc.push_back(kDefaultAttachmentDesc);
+ attachment_desc.back().format = depth_stencil_format_;
+
+ depth_refer.attachment = static_cast<uint32_t>(attachment_desc.size() - 1);
+ depth_refer.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+ subpass_desc.pDepthStencilAttachment = &depth_refer;
+ }
+
+ VkRenderPassCreateInfo render_pass_info = {};
+ render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ render_pass_info.attachmentCount =
+ static_cast<uint32_t>(attachment_desc.size());
+ render_pass_info.pAttachments = attachment_desc.data();
+ render_pass_info.subpassCount = 1;
+ render_pass_info.pSubpasses = &subpass_desc;
+
+ if (vkCreateRenderPass(device_, &render_pass_info, nullptr, &render_pass_) !=
+ VK_SUCCESS) {
+ return Result("Vulkan::Calling vkCreateRenderPass Fail");
+ }
+
+ return {};
+}
+
+VkPipelineDepthStencilStateCreateInfo
+GraphicsPipeline::GetPipelineDepthStencilInfo() {
+ VkPipelineDepthStencilStateCreateInfo depthstencil_info = {};
+ // TODO(jaebaek): Depth/stencil test setup should be come from the
+ // PipelineData.
+ depthstencil_info.depthTestEnable = VK_TRUE;
+ depthstencil_info.depthWriteEnable = VK_TRUE;
+ depthstencil_info.depthCompareOp = VK_COMPARE_OP_LESS;
+ depthstencil_info.depthBoundsTestEnable = VK_FALSE;
+ depthstencil_info.stencilTestEnable = VK_FALSE;
+ return depthstencil_info;
+}
+
+VkPipelineColorBlendAttachmentState
+GraphicsPipeline::GetPipelineColorBlendAttachmentState() {
+ VkPipelineColorBlendAttachmentState colorblend_attachment = {};
+ // TODO(jaebaek): Update blend state should be come from the PipelineData.
+ colorblend_attachment.blendEnable = VK_FALSE;
+ colorblend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO;
+ colorblend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
+ colorblend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
+ colorblend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+ colorblend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+ colorblend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
+ colorblend_attachment.colorWriteMask =
+ VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ return colorblend_attachment;
+}
+
+Result GraphicsPipeline::CreateVkGraphicsPipeline() {
+ if (pipeline_ != VK_NULL_HANDLE)
+ return Result("Vulkan::Pipeline already created");
+
+ Result r = CreatePipelineLayout();
+ if (!r.IsSuccess())
+ return r;
+
+ VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
+ vertex_input_info.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertex_input_info.vertexBindingDescriptionCount = 1;
+
+ VkVertexInputBindingDescription vertex_binding_desc = {};
+ if (vertex_buffer_) {
+ vertex_binding_desc = vertex_buffer_->GetVertexInputBinding();
+ auto vertex_attr_desc = vertex_buffer_->GetVertexInputAttr();
+
+ 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;
+ }
+
+ VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {};
+ input_assembly_info.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ // TODO(jaebaek): Handle the given index if exists.
+ input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ input_assembly_info.primitiveRestartEnable = VK_FALSE;
+
+ VkViewport viewport = {0,
+ 0,
+ static_cast<float>(frame_->GetWidth()),
+ static_cast<float>(frame_->GetHeight()),
+ 0,
+ 1};
+
+ VkRect2D scissor = {{0, 0}, {frame_->GetWidth(), frame_->GetHeight()}};
+
+ VkPipelineViewportStateCreateInfo viewport_info = {};
+ viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewport_info.viewportCount = 1;
+ viewport_info.pViewports = &viewport;
+ viewport_info.scissorCount = 1;
+ viewport_info.pScissors = &scissor;
+
+ VkGraphicsPipelineCreateInfo pipeline_info = {};
+ pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ pipeline_info.stageCount = static_cast<uint32_t>(shader_stage_info_.size());
+ pipeline_info.pStages = shader_stage_info_.data();
+ pipeline_info.pVertexInputState = &vertex_input_info;
+ pipeline_info.pInputAssemblyState = &input_assembly_info;
+ pipeline_info.pViewportState = &viewport_info;
+ pipeline_info.pRasterizationState = &kDefaultRasterizationInfo;
+ pipeline_info.pMultisampleState = &kMultisampleInfo;
+
+ VkPipelineDepthStencilStateCreateInfo depthstencil_info;
+ if (depth_stencil_format_ != VK_FORMAT_UNDEFINED) {
+ depthstencil_info = GetPipelineDepthStencilInfo();
+ pipeline_info.pDepthStencilState = &depthstencil_info;
+ }
+
+ VkPipelineColorBlendStateCreateInfo colorblend_info = {};
+ VkPipelineColorBlendAttachmentState colorblend_attachment;
+ if (color_format_ != VK_FORMAT_UNDEFINED) {
+ colorblend_attachment = GetPipelineColorBlendAttachmentState();
+
+ colorblend_info.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ colorblend_info.logicOpEnable = VK_FALSE;
+ colorblend_info.logicOp = VK_LOGIC_OP_COPY;
+ colorblend_info.attachmentCount = 1;
+ colorblend_info.pAttachments = &colorblend_attachment;
+ pipeline_info.pColorBlendState = &colorblend_info;
+ }
+
+ pipeline_info.layout = pipeline_layout_;
+ pipeline_info.renderPass = render_pass_;
+ pipeline_info.subpass = 0;
+
+ if (vkCreateGraphicsPipelines(device_, VK_NULL_HANDLE, 1, &pipeline_info,
+ nullptr, &pipeline_) != VK_SUCCESS) {
+ return Result("Vulkan::Calling vkCreateGraphicsPipelines Fail");
+ }
+
+ return {};
+}
+
+Result GraphicsPipeline::Initialize(uint32_t width,
+ uint32_t height,
+ VkCommandPool pool,
+ VkQueue queue) {
+ Result r = Pipeline::InitializeCommandBuffer(pool, queue);
+ if (!r.IsSuccess())
+ return r;
+
+ r = CreateRenderPass();
+ if (!r.IsSuccess())
+ return r;
+
+ frame_ = MakeUnique<FrameBuffer>(device_, width, height);
+ r = frame_->Initialize(render_pass_, color_format_, depth_stencil_format_,
+ memory_properties_);
+ if (!r.IsSuccess())
+ return r;
+
+ frame_width_ = width;
+ frame_height_ = height;
+
+ return {};
+}
+
+void GraphicsPipeline::SetBuffer(BufferType type,
+ uint8_t location,
+ const Format& format,
+ const std::vector<Value>& values) {
+ // TODO(jaebaek): Handle indices data.
+ if (type != BufferType::kVertexData)
+ return;
+
+ if (!vertex_buffer_)
+ vertex_buffer_ = MakeUnique<VertexBuffer>(device_);
+
+ vertex_buffer_->SetData(location, format, values);
+}
+
+Result GraphicsPipeline::SendBufferDataIfNeeded() {
+ if (!vertex_buffer_)
+ return {};
+
+ if (vertex_buffer_->VertexDataSent())
+ return {};
+
+ Result r = command_->BeginIfNotInRecording();
+ if (!r.IsSuccess())
+ return r;
+
+ DeactivateRenderPassIfNeeded();
+
+ // TODO(jaebaek): Send indices data too.
+ return vertex_buffer_->SendVertexData(command_->GetCommandBuffer(),
+ memory_properties_);
+}
+
+void GraphicsPipeline::ActivateRenderPassIfNeeded() {
+ if (render_pass_state_ == RenderPassState::kActive)
+ return;
+
+ VkRenderPassBeginInfo render_begin_info = {};
+ render_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+ render_begin_info.renderPass = render_pass_;
+ render_begin_info.framebuffer = frame_->GetFrameBuffer();
+ render_begin_info.renderArea = {{0, 0}, {frame_width_, frame_height_}};
+ vkCmdBeginRenderPass(command_->GetCommandBuffer(), &render_begin_info,
+ VK_SUBPASS_CONTENTS_INLINE);
+ render_pass_state_ = RenderPassState::kActive;
+}
+
+void GraphicsPipeline::DeactivateRenderPassIfNeeded() {
+ if (render_pass_state_ == RenderPassState::kInactive)
+ return;
+
+ vkCmdEndRenderPass(command_->GetCommandBuffer());
+ render_pass_state_ = RenderPassState::kInactive;
+}
+
+Result GraphicsPipeline::SetClearColor(float r, float g, float b, float a) {
+ if (color_format_ == VK_FORMAT_UNDEFINED) {
+ return Result(
+ "Vulkan::ClearColorCommand No Color Buffer for FrameBuffer Exists");
+ }
+
+ clear_color_r_ = r;
+ clear_color_g_ = g;
+ clear_color_b_ = b;
+ clear_color_a_ = a;
+ return {};
+}
+
+Result GraphicsPipeline::SetClearStencil(uint32_t stencil) {
+ if (depth_stencil_format_ == VK_FORMAT_UNDEFINED) {
+ return Result(
+ "Vulkan::ClearStencilCommand No DepthStencil Buffer for FrameBuffer "
+ "Exists");
+ }
+
+ clear_stencil_ = stencil;
+ return {};
+}
+
+Result GraphicsPipeline::SetClearDepth(float depth) {
+ if (depth_stencil_format_ == VK_FORMAT_UNDEFINED) {
+ return Result(
+ "Vulkan::ClearStencilCommand No DepthStencil Buffer for FrameBuffer "
+ "Exists");
+ }
+
+ clear_depth_ = depth;
+ return {};
+}
+
+Result GraphicsPipeline::Clear() {
+ if (color_format_ == VK_FORMAT_UNDEFINED &&
+ depth_stencil_format_ == VK_FORMAT_UNDEFINED) {
+ return Result(
+ "Vulkan::ClearColorCommand No Color nor DepthStencil Buffer for "
+ "FrameBuffer Exists");
+ }
+
+ if (color_format_ != VK_FORMAT_UNDEFINED) {
+ VkClearValue clear_value;
+ clear_value.color = {
+ {clear_color_r_, clear_color_g_, clear_color_b_, clear_color_a_}};
+ Result r = ClearBuffer(clear_value, VK_IMAGE_ASPECT_COLOR_BIT);
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ if (depth_stencil_format_ == VK_FORMAT_UNDEFINED)
+ return {};
+
+ VkClearValue clear_value;
+ clear_value.depthStencil = {clear_depth_, clear_stencil_};
+ return ClearBuffer(clear_value,
+ VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
+}
+
+Result GraphicsPipeline::ClearBuffer(const VkClearValue& clear_value,
+ VkImageAspectFlags aspect) {
+ Result r = command_->BeginIfNotInRecording();
+ if (!r.IsSuccess())
+ return r;
+
+ // TODO(jaebaek): When multiple clear and draw commands exist, handle
+ // begin/end render pass properly.
+ ActivateRenderPassIfNeeded();
+
+ VkClearAttachment clear_attachment = {};
+ clear_attachment.aspectMask = aspect;
+ clear_attachment.colorAttachment = 0;
+ clear_attachment.clearValue = clear_value;
+
+ VkClearRect clear_rect;
+ clear_rect.rect = {{0, 0}, {frame_width_, frame_height_}};
+ clear_rect.baseArrayLayer = 0;
+ clear_rect.layerCount = 1;
+
+ vkCmdClearAttachments(command_->GetCommandBuffer(), 1, &clear_attachment, 1,
+ &clear_rect);
+
+ return {};
+}
+
+Result GraphicsPipeline::Draw() {
+ // TODO(jaebaek): Handle primitive topology.
+ if (pipeline_ == VK_NULL_HANDLE) {
+ Result r = CreateVkGraphicsPipeline();
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ Result r = SendBufferDataIfNeeded();
+ if (!r.IsSuccess())
+ return r;
+
+ r = command_->BeginIfNotInRecording();
+ if (!r.IsSuccess())
+ return r;
+
+ ActivateRenderPassIfNeeded();
+
+ vkCmdBindPipeline(command_->GetCommandBuffer(),
+ VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_);
+
+ uint32_t vertex_count = 0;
+ uint32_t instance_count = 0;
+ if (vertex_buffer_) {
+ vertex_buffer_->BindToCommandBuffer(command_->GetCommandBuffer());
+ vertex_count = static_cast<uint32_t>(vertex_buffer_->GetVertexCount());
+ instance_count = 1;
+ }
+
+ vkCmdDraw(command_->GetCommandBuffer(), vertex_count, instance_count, 0, 0);
+
+ return {};
+}
+
+Result GraphicsPipeline::SubmitProbeCommand() {
+ Result r = command_->BeginIfNotInRecording();
+ if (!r.IsSuccess())
+ return r;
+
+ ActivateRenderPassIfNeeded();
+ DeactivateRenderPassIfNeeded();
+
+ r = frame_->CopyColorImageToHost(command_->GetCommandBuffer());
+ if (!r.IsSuccess())
+ return r;
+
+ r = command_->End();
+ if (!r.IsSuccess())
+ return r;
+
+ return command_->SubmitAndReset();
+}
+
+Result GraphicsPipeline::VerifyPixels(const uint32_t x,
+ const uint32_t y,
+ const uint32_t width,
+ const uint32_t height,
+ const ProbeCommand* command) {
+ const uint32_t stride = VkFormatToByteSize(color_format_);
+
+ // TODO(jaebaek): Support all VkFormat
+ const uint8_t* ptr = static_cast<const uint8_t*>(frame_->GetColorBufferPtr());
+ uint32_t count_of_invalid_pixels = 0;
+ uint32_t first_invalid_i = 0;
+ uint32_t first_invalid_j = 0;
+ for (uint32_t j = 0; j < height; ++j) {
+ const uint8_t* p = ptr + stride * frame_->GetWidth() * (j + y) + stride * x;
+ for (uint32_t i = 0; i < width; ++i) {
+ // TODO(jaebaek): Get actual pixel values based on frame buffer formats.
+ if (!IsFloatPixelEqualInt(command->GetR(), p[stride * i]) ||
+ !IsFloatPixelEqualInt(command->GetG(), p[stride * i + 1]) ||
+ !IsFloatPixelEqualInt(command->GetB(), p[stride * i + 2]) ||
+ (command->IsRGBA() &&
+ !IsFloatPixelEqualInt(command->GetA(), p[stride * i + 3]))) {
+ if (!count_of_invalid_pixels) {
+ first_invalid_i = i;
+ first_invalid_j = j;
+ }
+ ++count_of_invalid_pixels;
+ }
+ }
+ }
+
+ if (count_of_invalid_pixels) {
+ const uint8_t* p =
+ ptr + stride * frame_->GetWidth() * (first_invalid_j + y) + stride * x;
+ return Result(
+ "Probe failed at: " + std::to_string(first_invalid_i + x) + ", " +
+ std::to_string(first_invalid_j + y) + "\n" +
+ " Expected RGBA: " + std::to_string(command->GetR() * 255) + ", " +
+ std::to_string(command->GetG() * 255) + ", " +
+ std::to_string(command->GetB() * 255) +
+ (command->IsRGBA() ? ", " + std::to_string(command->GetA() * 255) +
+ "\n Actual RGBA: "
+ : "\n Actual RGB: ") +
+ std::to_string(static_cast<int>(p[stride * first_invalid_i])) + ", " +
+ std::to_string(static_cast<int>(p[stride * first_invalid_i + 1])) +
+ ", " +
+ std::to_string(static_cast<int>(p[stride * first_invalid_i + 2])) +
+ (command->IsRGBA() ? ", " + std::to_string(static_cast<int>(
+ p[stride * first_invalid_i + 3]))
+ : "") +
+ "\n" + "Probe failed in " + std::to_string(count_of_invalid_pixels) +
+ " pixels");
+ }
+
+ return {};
+}
+
+Result GraphicsPipeline::Probe(const ProbeCommand* command) {
+ uint32_t x = 0;
+ uint32_t y = 0;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ const uint32_t frame_width = frame_->GetWidth();
+ const uint32_t frame_height = frame_->GetHeight();
+
+ if (command->IsWholeWindow()) {
+ width = frame_width;
+ height = frame_height;
+ } else if (command->IsRelative()) {
+ x = static_cast<uint32_t>(frame_width * command->GetX());
+ y = static_cast<uint32_t>(frame_height * command->GetY());
+ width = static_cast<uint32_t>(frame_width * command->GetWidth());
+ height = static_cast<uint32_t>(frame_height * command->GetHeight());
+ } else {
+ x = static_cast<uint32_t>(command->GetX());
+ y = static_cast<uint32_t>(command->GetY());
+ width = static_cast<uint32_t>(command->GetWidth());
+ height = static_cast<uint32_t>(command->GetHeight());
+ }
+
+ if (x + width > frame_width || y + height > frame_height) {
+ return Result(
+ "Vulkan::Probe Position(" + std::to_string(x + width - 1) + ", " +
+ std::to_string(y + height - 1) + ") is out of framebuffer scope (" +
+ std::to_string(frame_width) + "," + std::to_string(frame_height) + ")");
+ }
+
+ Result r = SubmitProbeCommand();
+ if (!r.IsSuccess())
+ return r;
+
+ return VerifyPixels(x, y, width, height, command);
+}
+
+void GraphicsPipeline::Shutdown() {
+ DeactivateRenderPassIfNeeded();
+
+ Result r = command_->End();
+ if (r.IsSuccess())
+ command_->SubmitAndReset();
+
+ Pipeline::Shutdown();
+ frame_->Shutdown();
+ vkDestroyRenderPass(device_, render_pass_, nullptr);
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/graphics_pipeline.h b/src/vulkan/graphics_pipeline.h
new file mode 100644
index 0000000..9608dc9
--- /dev/null
+++ b/src/vulkan/graphics_pipeline.h
@@ -0,0 +1,121 @@
+// Copyright 2018 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_GRAPHICS_PIPELINE_H_
+#define SRC_VULKAN_GRAPHICS_PIPELINE_H_
+
+#include <memory>
+#include <vector>
+
+#include "amber/result.h"
+#include "src/buffer_data.h"
+#include "src/format.h"
+#include "src/value.h"
+#include "src/vulkan/frame_buffer.h"
+#include "src/vulkan/pipeline.h"
+#include "src/vulkan/vertex_buffer.h"
+#include "vulkan/vulkan.h"
+
+namespace amber {
+
+class ProbeCommand;
+
+namespace vulkan {
+
+class GraphicsPipeline : public Pipeline {
+ public:
+ GraphicsPipeline(PipelineType type,
+ VkDevice device,
+ const VkPhysicalDeviceMemoryProperties& properties,
+ VkFormat color_format,
+ VkFormat depth_stencil_format,
+ std::vector<VkPipelineShaderStageCreateInfo>);
+ ~GraphicsPipeline() override;
+
+ Result Initialize(uint32_t width,
+ uint32_t height,
+ VkCommandPool pool,
+ VkQueue queue);
+ void Shutdown() override;
+
+ void SetBuffer(BufferType type,
+ uint8_t location,
+ const Format& format,
+ const std::vector<Value>& values);
+
+ Result Clear();
+ Result ClearBuffer(const VkClearValue& clear_value,
+ VkImageAspectFlags aspect);
+ Result Probe(const ProbeCommand*);
+
+ VkFormat GetColorFormat() const { return color_format_; }
+ VkFormat GetDepthStencilFormat() const { return depth_stencil_format_; }
+
+ Result SetClearColor(float r, float g, float b, float a);
+ Result SetClearStencil(uint32_t stencil);
+ Result SetClearDepth(float depth);
+
+ Result Draw();
+
+ private:
+ enum class RenderPassState : uint8_t {
+ kActive = 0,
+ kInactive,
+ };
+
+ Result CreateVkGraphicsPipeline();
+
+ Result CreateRenderPass();
+ void ActivateRenderPassIfNeeded();
+ void DeactivateRenderPassIfNeeded();
+
+ Result SendBufferDataIfNeeded();
+
+ // TODO(jaebaek): Implement image/ssbo probe.
+ Result SubmitProbeCommand();
+ Result VerifyPixels(const uint32_t x,
+ const uint32_t y,
+ const uint32_t width,
+ const uint32_t height,
+ const ProbeCommand* command);
+
+ VkPipelineDepthStencilStateCreateInfo GetPipelineDepthStencilInfo();
+ VkPipelineColorBlendAttachmentState GetPipelineColorBlendAttachmentState();
+
+ VkRenderPass render_pass_ = VK_NULL_HANDLE;
+ RenderPassState render_pass_state_ = RenderPassState::kInactive;
+
+ std::unique_ptr<FrameBuffer> frame_;
+ VkFormat color_format_;
+ VkFormat depth_stencil_format_;
+
+ std::vector<VkPipelineShaderStageCreateInfo> shader_stage_info_;
+
+ uint32_t frame_width_ = 0;
+ uint32_t frame_height_ = 0;
+
+ float clear_color_r_ = 0;
+ float clear_color_g_ = 0;
+ float clear_color_b_ = 0;
+ float clear_color_a_ = 0;
+ uint32_t clear_stencil_ = 0;
+ float clear_depth_ = 1.0f;
+
+ std::unique_ptr<VertexBuffer> vertex_buffer_;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_GRAPHICS_PIPELINE_H_
diff --git a/src/vulkan/image.cc b/src/vulkan/image.cc
new file mode 100644
index 0000000..4722187
--- /dev/null
+++ b/src/vulkan/image.cc
@@ -0,0 +1,151 @@
+// Copyright 2018 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.h"
+
+#include <limits>
+
+#include "src/vulkan/format_data.h"
+
+namespace amber {
+namespace vulkan {
+namespace {
+
+const VkImageCreateInfo kDefaultImageInfo = {
+ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, /* sType */
+ nullptr, /* pNext */
+ 0, /* flags */
+ VK_IMAGE_TYPE_2D, /* imageType */
+ VK_FORMAT_R8G8B8A8_UNORM, /* format */
+ {250, 250, 1}, /* extent */
+ 1, /* mipLevels */
+ 1, /* arrayLayers */
+ VK_SAMPLE_COUNT_1_BIT, /* samples */
+ VK_IMAGE_TILING_OPTIMAL, /* tiling */
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, /* usage */
+ VK_SHARING_MODE_EXCLUSIVE, /* sharingMode */
+ 0, /* queueFamilyIndexCount */
+ nullptr, /* pQueueFamilyIndices */
+ VK_IMAGE_LAYOUT_UNDEFINED, /* initialLayout */
+};
+
+} // namespace
+
+Image::Image(VkDevice device,
+ VkFormat format,
+ uint32_t x,
+ uint32_t y,
+ uint32_t z,
+ const VkPhysicalDeviceMemoryProperties& properties)
+ : Resource(device, x * y * z * VkFormatToByteSize(format), properties),
+ image_info_(kDefaultImageInfo) {
+ image_info_.format = format;
+ image_info_.extent = {x, y, z};
+}
+
+Image::~Image() = default;
+
+Result Image::Initialize(VkImageUsageFlags usage) {
+ if (image_ != VK_NULL_HANDLE)
+ return Result("Vulkan::Image was already initalized");
+
+ image_info_.usage = usage;
+
+ if (vkCreateImage(GetDevice(), &image_info_, nullptr, &image_) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkCreateImage Fail");
+
+ AllocateResult allocate_result = AllocateAndBindMemoryToVkImage(
+ image_, &memory_, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, false);
+ if (!allocate_result.r.IsSuccess())
+ return allocate_result.r;
+
+ Result r = CreateVkImageView();
+ if (!r.IsSuccess())
+ return r;
+
+ if (CheckMemoryHostAccessible(allocate_result.memory_type_index)) {
+ is_image_host_accessible_ = true;
+ return MapMemory(memory_);
+ }
+
+ is_image_host_accessible_ = false;
+ return Resource::Initialize();
+}
+
+Result Image::CreateVkImageView() {
+ VkImageViewCreateInfo image_view_info = {};
+ 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.format = image_info_.format;
+ image_view_info.components = {
+ VK_COMPONENT_SWIZZLE_R,
+ VK_COMPONENT_SWIZZLE_G,
+ VK_COMPONENT_SWIZZLE_B,
+ VK_COMPONENT_SWIZZLE_A,
+ };
+ image_view_info.subresourceRange = {
+ VK_IMAGE_ASPECT_COLOR_BIT, /* aspectMask */
+ 0, /* baseMipLevel */
+ 1, /* levelCount */
+ 0, /* baseArrayLayer */
+ 1, /* layerCount */
+ };
+
+ if (vkCreateImageView(GetDevice(), &image_view_info, nullptr, &view_) !=
+ VK_SUCCESS) {
+ return Result("Vulkan::Calling vkCreateImageView Fail");
+ }
+
+ return {};
+}
+
+void Image::Shutdown() {
+ vkDestroyImageView(GetDevice(), view_, nullptr);
+ vkDestroyImage(GetDevice(), image_, nullptr);
+ vkFreeMemory(GetDevice(), memory_, nullptr);
+
+ view_ = VK_NULL_HANDLE;
+ image_ = VK_NULL_HANDLE;
+ memory_ = VK_NULL_HANDLE;
+}
+
+Result Image::CopyToHost(VkCommandBuffer command) {
+ if (is_image_host_accessible_)
+ return {};
+
+ VkBufferImageCopy copy_region = {};
+ copy_region.bufferOffset = 0;
+ copy_region.bufferRowLength = 0;
+ copy_region.bufferImageHeight = 0;
+ copy_region.imageSubresource = {
+ VK_IMAGE_ASPECT_COLOR_BIT, /* aspectMask */
+ 0, /* mipLevel */
+ 0, /* baseArrayLayer */
+ 1, /* layerCount */
+ };
+ copy_region.imageOffset = {0, 0, 0};
+ copy_region.imageExtent = {image_info_.extent.width,
+ image_info_.extent.height, 1};
+
+ vkCmdCopyImageToBuffer(command, image_, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ GetHostAccessibleBuffer(), 1, ©_region);
+
+ return {};
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/image.h b/src/vulkan/image.h
new file mode 100644
index 0000000..c946af4
--- /dev/null
+++ b/src/vulkan/image.h
@@ -0,0 +1,68 @@
+// Copyright 2018 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_H_
+#define SRC_VULKAN_IMAGE_H_
+
+#include "amber/result.h"
+#include "src/vulkan/resource.h"
+#include "vulkan/vulkan.h"
+
+namespace amber {
+namespace vulkan {
+
+class Image : public Resource {
+ public:
+ Image(VkDevice device,
+ VkFormat format,
+ uint32_t x,
+ uint32_t y,
+ uint32_t z,
+ const VkPhysicalDeviceMemoryProperties& properties);
+ ~Image() override;
+
+ Result Initialize(VkImageUsageFlags usage);
+ VkImage GetVkImage() const { return image_; }
+ VkImageView GetVkImageView() const { return view_; }
+
+ // TODO(jaebaek): Determine copy all or partial data
+ Result CopyToHost(VkCommandBuffer command);
+
+ // TODO(jaebaek): Implement CopyToDevice
+
+ // Resource
+ VkDeviceMemory GetHostAccessMemory() const override {
+ if (is_image_host_accessible_)
+ return memory_;
+
+ return Resource::GetHostAccessMemory();
+ }
+
+ void Shutdown() override;
+
+ private:
+ Result CreateVkImageView();
+
+ VkImageCreateInfo image_info_;
+
+ VkImage image_ = VK_NULL_HANDLE;
+ VkImageView view_ = VK_NULL_HANDLE;
+ VkDeviceMemory memory_ = VK_NULL_HANDLE;
+ bool is_image_host_accessible_ = false;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_IMAGE_H_
diff --git a/src/vulkan/pipeline.cc b/src/vulkan/pipeline.cc
new file mode 100644
index 0000000..e16c277
--- /dev/null
+++ b/src/vulkan/pipeline.cc
@@ -0,0 +1,68 @@
+// Copyright 2018 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 <limits>
+
+#include "src/command.h"
+#include "src/make_unique.h"
+#include "src/vulkan/graphics_pipeline.h"
+
+namespace amber {
+namespace vulkan {
+
+Pipeline::Pipeline(PipelineType type,
+ VkDevice device,
+ const VkPhysicalDeviceMemoryProperties& properties)
+ : device_(device), memory_properties_(properties), pipeline_type_(type) {}
+
+Pipeline::~Pipeline() = default;
+
+GraphicsPipeline* Pipeline::AsGraphics() {
+ return static_cast<GraphicsPipeline*>(this);
+}
+
+Result Pipeline::InitializeCommandBuffer(VkCommandPool pool, VkQueue queue) {
+ command_ = MakeUnique<CommandBuffer>(device_, pool, queue);
+ Result r = command_->Initialize();
+ if (!r.IsSuccess())
+ return r;
+
+ return {};
+}
+
+void Pipeline::Shutdown() {
+ // TODO(jaebaek): destroy pipeline_cache_ and pipeline_
+ command_->Shutdown();
+ vkDestroyPipelineLayout(device_, pipeline_layout_, nullptr);
+}
+
+Result Pipeline::CreatePipelineLayout() {
+ VkPipelineLayoutCreateInfo pipeline_layout_info = {};
+ pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipeline_layout_info.setLayoutCount = 0;
+ pipeline_layout_info.pSetLayouts = nullptr;
+ // TODO(jaebaek): Push constant for pipeline_layout_info.
+
+ if (vkCreatePipelineLayout(device_, &pipeline_layout_info, nullptr,
+ &pipeline_layout_) != VK_SUCCESS) {
+ return Result("Vulkan::Calling vkCreatePipelineLayout Fail");
+ }
+
+ return {};
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/pipeline.h b/src/vulkan/pipeline.h
new file mode 100644
index 0000000..38a234a
--- /dev/null
+++ b/src/vulkan/pipeline.h
@@ -0,0 +1,67 @@
+// Copyright 2018 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_PIPELINE_H_
+#define SRC_VULKAN_PIPELINE_H_
+
+#include <memory>
+#include <vector>
+
+#include "amber/result.h"
+#include "src/engine.h"
+#include "src/vulkan/command.h"
+#include "vulkan/vulkan.h"
+
+namespace amber {
+namespace vulkan {
+
+class GraphicsPipeline;
+
+class Pipeline {
+ public:
+ virtual ~Pipeline();
+
+ bool IsGraphics() const { return pipeline_type_ == PipelineType::kGraphics; }
+ bool IsCompute() const { return pipeline_type_ == PipelineType::kCompute; }
+
+ GraphicsPipeline* AsGraphics();
+
+ virtual void Shutdown();
+
+ protected:
+ Pipeline(PipelineType type,
+ VkDevice device,
+ const VkPhysicalDeviceMemoryProperties& properties);
+ Result InitializeCommandBuffer(VkCommandPool pool, VkQueue queue);
+
+ Result CreatePipelineLayout();
+
+ VkPipelineCache pipeline_cache_ = VK_NULL_HANDLE;
+ VkPipeline pipeline_ = VK_NULL_HANDLE;
+ VkPipelineLayout pipeline_layout_ = VK_NULL_HANDLE;
+
+ std::vector<VkDescriptorSetLayout> descriptor_set_layout_;
+
+ VkDevice device_ = VK_NULL_HANDLE;
+ VkPhysicalDeviceMemoryProperties memory_properties_;
+ std::unique_ptr<CommandBuffer> command_;
+
+ private:
+ PipelineType pipeline_type_;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_PIPELINE_H_
diff --git a/src/vulkan/resource.cc b/src/vulkan/resource.cc
new file mode 100644
index 0000000..69a21a5
--- /dev/null
+++ b/src/vulkan/resource.cc
@@ -0,0 +1,209 @@
+// Copyright 2018 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/resource.h"
+
+#include <limits>
+
+#include "src/make_unique.h"
+#include "src/vulkan/format_data.h"
+
+namespace amber {
+namespace vulkan {
+
+Resource::Resource(VkDevice device,
+ size_t size,
+ const VkPhysicalDeviceMemoryProperties& properties)
+ : device_(device), size_(size), physical_memory_properties_(properties) {}
+
+Resource::~Resource() = default;
+
+void Resource::Shutdown() {
+ UnMapMemory(host_accessible_memory_);
+ vkDestroyBuffer(device_, host_accessible_buffer_, nullptr);
+ vkFreeMemory(device_, host_accessible_memory_, nullptr);
+}
+
+Result Resource::Initialize() {
+ Result r = CreateVkBuffer(
+ &host_accessible_buffer_,
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+ if (!r.IsSuccess())
+ return r;
+
+ AllocateResult allocate_result = AllocateAndBindMemoryToVkBuffer(
+ host_accessible_buffer_, &host_accessible_memory_,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+ VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ true);
+ if (!allocate_result.r.IsSuccess())
+ return allocate_result.r;
+
+ return MapMemory(host_accessible_memory_);
+}
+
+Result Resource::CreateVkBuffer(VkBuffer* buffer, VkBufferUsageFlags usage) {
+ if (buffer == nullptr)
+ return Result("Vulkan::Given VkBuffer pointer is nullptr");
+
+ VkBufferCreateInfo buffer_info = {};
+ buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ buffer_info.size = size_;
+ buffer_info.usage = usage;
+
+ if (vkCreateBuffer(device_, &buffer_info, nullptr, buffer) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkCreateBuffer Fail");
+
+ return {};
+}
+
+uint32_t Resource::ChooseMemory(uint32_t memory_type_bits,
+ VkMemoryPropertyFlags flags,
+ bool force_flags) {
+ // Based on Vulkan spec about VkMemoryRequirements, N th bit of
+ // |memory_type_bits| is 1 where N can be the proper memory type index.
+ // This code is looking for the first non-zero bit whose memory type
+ // VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT property. If not exists,
+ // it returns the first non-zero bit.
+ uint32_t first_non_zero = std::numeric_limits<uint32_t>::max();
+ uint32_t memory_type_index = 0;
+ while (memory_type_bits) {
+ if (memory_type_bits % 2) {
+ if (first_non_zero == std::numeric_limits<uint32_t>::max())
+ first_non_zero = memory_type_index;
+
+ if ((physical_memory_properties_.memoryTypes[memory_type_index]
+ .propertyFlags &
+ flags) == flags) {
+ return memory_type_index;
+ }
+ }
+
+ ++memory_type_index;
+ memory_type_bits >>= 1;
+ }
+
+ if (force_flags)
+ return std::numeric_limits<uint32_t>::max();
+
+ return first_non_zero;
+}
+
+const VkMemoryRequirements Resource::GetVkBufferMemoryRequirements(
+ VkBuffer buffer) const {
+ VkMemoryRequirements requirement;
+ vkGetBufferMemoryRequirements(device_, buffer, &requirement);
+ return requirement;
+}
+
+const VkMemoryRequirements Resource::GetVkImageMemoryRequirements(
+ VkImage image) const {
+ VkMemoryRequirements requirement;
+ vkGetImageMemoryRequirements(device_, image, &requirement);
+ return requirement;
+}
+
+Resource::AllocateResult Resource::AllocateAndBindMemoryToVkBuffer(
+ VkBuffer buffer,
+ VkDeviceMemory* memory,
+ VkMemoryPropertyFlags flags,
+ bool force_flags) {
+ if (buffer == VK_NULL_HANDLE)
+ return {Result("Vulkan::Given VkBuffer is VK_NULL_HANDLE"), 0};
+
+ if (memory == nullptr)
+ return {Result("Vulkan::Given VkDeviceMemory pointer is nullptr"), 0};
+
+ auto requirement = GetVkBufferMemoryRequirements(buffer);
+
+ uint32_t memory_type_index =
+ ChooseMemory(requirement.memoryTypeBits, flags, force_flags);
+ if (memory_type_index == std::numeric_limits<uint32_t>::max())
+ return {Result("Vulkan::Find Proper Memory Fail"), 0};
+
+ Result r = AllocateMemory(memory, requirement.size, memory_type_index);
+ if (!r.IsSuccess())
+ return {r, 0};
+
+ return {BindMemoryToVkBuffer(buffer, *memory), memory_type_index};
+}
+
+Result Resource::AllocateMemory(VkDeviceMemory* memory,
+ VkDeviceSize size,
+ uint32_t memory_type_index) {
+ VkMemoryAllocateInfo alloc_info = {};
+ alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ alloc_info.allocationSize = size;
+ alloc_info.memoryTypeIndex = memory_type_index;
+ if (vkAllocateMemory(device_, &alloc_info, nullptr, memory) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkAllocateMemory Fail");
+
+ return {};
+}
+
+Result Resource::BindMemoryToVkBuffer(VkBuffer buffer, VkDeviceMemory memory) {
+ if (vkBindBufferMemory(device_, buffer, memory, 0) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkBindBufferMemory Fail");
+
+ return {};
+}
+
+Resource::AllocateResult Resource::AllocateAndBindMemoryToVkImage(
+ VkImage image,
+ VkDeviceMemory* memory,
+ VkMemoryPropertyFlags flags,
+ bool force_flags) {
+ if (image == nullptr)
+ return {Result("Vulkan::Given VkImage pointer is nullptr"), 0};
+
+ if (memory == nullptr)
+ return {Result("Vulkan::Given VkDeviceMemory pointer is nullptr"), 0};
+
+ auto requirement = GetVkImageMemoryRequirements(image);
+
+ uint32_t memory_type_index =
+ ChooseMemory(requirement.memoryTypeBits, flags, force_flags);
+ if (memory_type_index == std::numeric_limits<uint32_t>::max())
+ return {Result("Vulkan::Find Proper Memory Fail"), 0};
+
+ Result r = AllocateMemory(memory, requirement.size, memory_type_index);
+ if (!r.IsSuccess())
+ return {r, 0};
+
+ return {BindMemoryToVkImage(image, *memory), memory_type_index};
+}
+
+Result Resource::BindMemoryToVkImage(VkImage image, VkDeviceMemory memory) {
+ if (vkBindImageMemory(device_, image, memory, 0) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkBindImageMemory Fail");
+
+ return {};
+}
+
+Result Resource::MapMemory(VkDeviceMemory memory) {
+ if (vkMapMemory(device_, memory, 0, VK_WHOLE_SIZE, 0, &memory_ptr_) !=
+ VK_SUCCESS) {
+ return Result("Vulkan::Calling vkMapMemory Fail");
+ }
+
+ return {};
+}
+
+void Resource::UnMapMemory(VkDeviceMemory memory) {
+ vkUnmapMemory(device_, memory);
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/resource.h b/src/vulkan/resource.h
new file mode 100644
index 0000000..c2530ae
--- /dev/null
+++ b/src/vulkan/resource.h
@@ -0,0 +1,101 @@
+// Copyright 2018 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_RESOURCE_H_
+#define SRC_VULKAN_RESOURCE_H_
+
+#include <memory>
+
+#include "amber/result.h"
+#include "vulkan/vulkan.h"
+
+namespace amber {
+namespace vulkan {
+
+class Resource {
+ public:
+ virtual ~Resource();
+
+ virtual VkDeviceMemory GetHostAccessMemory() const {
+ return host_accessible_memory_;
+ }
+
+ virtual void Shutdown();
+
+ void* HostAccessibleMemoryPtr() const { return memory_ptr_; }
+
+ protected:
+ Resource(VkDevice device,
+ size_t size,
+ const VkPhysicalDeviceMemoryProperties& properties);
+ Result Initialize();
+ Result CreateVkBuffer(VkBuffer* buffer, VkBufferUsageFlags usage);
+
+ VkDevice GetDevice() const { return device_; }
+ VkBuffer GetHostAccessibleBuffer() const { return host_accessible_buffer_; }
+
+ size_t GetSize() const { return size_; }
+
+ struct AllocateResult {
+ Result r;
+ uint32_t memory_type_index;
+ };
+
+ AllocateResult AllocateAndBindMemoryToVkBuffer(VkBuffer buffer,
+ VkDeviceMemory* memory,
+ VkMemoryPropertyFlags flags,
+ bool force_flags);
+ AllocateResult AllocateAndBindMemoryToVkImage(VkImage image,
+ VkDeviceMemory* memory,
+ VkMemoryPropertyFlags flags,
+ bool force_flags);
+
+ bool CheckMemoryHostAccessible(uint32_t memory_type_index) {
+ return (physical_memory_properties_.memoryTypes[memory_type_index]
+ .propertyFlags &
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ==
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ }
+
+ Result MapMemory(VkDeviceMemory memory);
+ void UnMapMemory(VkDeviceMemory memory);
+
+ private:
+ uint32_t ChooseMemory(uint32_t memory_type_bits,
+ VkMemoryPropertyFlags flags,
+ bool force_flags);
+ Result AllocateMemory(VkDeviceMemory* memory,
+ VkDeviceSize size,
+ uint32_t memory_type_index);
+
+ Result BindMemoryToVkBuffer(VkBuffer buffer, VkDeviceMemory memory);
+ const VkMemoryRequirements GetVkBufferMemoryRequirements(
+ VkBuffer buffer) const;
+
+ Result BindMemoryToVkImage(VkImage image, VkDeviceMemory memory);
+ const VkMemoryRequirements GetVkImageMemoryRequirements(VkImage image) const;
+
+ VkDevice device_ = VK_NULL_HANDLE;
+ size_t size_ = 0;
+ VkPhysicalDeviceMemoryProperties physical_memory_properties_;
+
+ VkBuffer host_accessible_buffer_ = VK_NULL_HANDLE;
+ VkDeviceMemory host_accessible_memory_ = VK_NULL_HANDLE;
+ void* memory_ptr_ = nullptr;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_RESOURCE_H_
diff --git a/src/vulkan/vertex_buffer.cc b/src/vulkan/vertex_buffer.cc
new file mode 100644
index 0000000..943cbb1
--- /dev/null
+++ b/src/vulkan/vertex_buffer.cc
@@ -0,0 +1,118 @@
+// Copyright 2018 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/vertex_buffer.h"
+
+#include <cassert>
+#include <cstring>
+
+#include "src/make_unique.h"
+#include "src/vulkan/bit_copy.h"
+#include "src/vulkan/format_data.h"
+
+namespace amber {
+namespace vulkan {
+
+VertexBuffer::VertexBuffer(VkDevice device) : device_(device) {}
+
+VertexBuffer::~VertexBuffer() = default;
+
+void VertexBuffer::SetData(uint8_t location,
+ const Format& format,
+ const std::vector<Value>& values) {
+ vertex_attr_desc_.emplace_back();
+ // TODO(jaebaek): Support multiple binding
+ vertex_attr_desc_.back().binding = 0;
+ vertex_attr_desc_.back().location = location;
+ vertex_attr_desc_.back().format = ToVkFormat(format.GetFormatType());
+ vertex_attr_desc_.back().offset = stride_in_bytes_;
+
+ stride_in_bytes_ += format.GetByteSize();
+
+ formats_.push_back(format);
+ data_.push_back(values);
+}
+
+void VertexBuffer::FillVertexBufferWithData(VkCommandBuffer command) {
+ // Send vertex data from host to device.
+ uint8_t* ptr = static_cast<uint8_t*>(buffer_->HostAccessibleMemoryPtr());
+ for (uint32_t i = 0; i < GetVertexCount(); ++i) {
+ for (uint32_t j = 0; j < formats_.size(); ++j) {
+ const auto pack_size = formats_[j].GetPackSize();
+ if (pack_size) {
+ BitCopy::CopyValueToBuffer(ptr, data_[j][i], 0, pack_size);
+ ptr += pack_size / 8;
+ continue;
+ }
+
+ const auto& components = formats_[j].GetComponents();
+ uint8_t bit_offset = 0;
+
+ for (uint32_t k = 0; k < components.size(); ++k) {
+ uint8_t bits = components[k].num_bits;
+ BitCopy::CopyValueToBuffer(ptr, data_[j][i * components.size() + k],
+ bit_offset, bits);
+
+ assert(k == components.size() - 1 ||
+ static_cast<uint32_t>(bit_offset) + static_cast<uint32_t>(bits) <
+ 256);
+ bit_offset += bits;
+ }
+
+ ptr += formats_[j].GetByteSize();
+ }
+ }
+
+ ptr = static_cast<uint8_t*>(buffer_->HostAccessibleMemoryPtr());
+ buffer_->CopyToDevice(command);
+}
+
+void VertexBuffer::BindToCommandBuffer(VkCommandBuffer command) {
+ const VkDeviceSize offset = 0;
+ const VkBuffer buffer = buffer_->GetVkBuffer();
+ // TODO(jaebaek): Support multiple binding
+ vkCmdBindVertexBuffers(command, 0, 1, &buffer, &offset);
+}
+
+Result VertexBuffer::SendVertexData(
+ VkCommandBuffer command,
+ const VkPhysicalDeviceMemoryProperties& properties) {
+ if (!is_vertex_data_pending_)
+ return Result("Vulkan::Vertices data was already sent");
+
+ const size_t n_vertices = GetVertexCount();
+ if (n_vertices == 0)
+ return Result("Vulkan::Data for VertexBuffer is empty");
+
+ size_t bytes = stride_in_bytes_ * n_vertices;
+
+ if (!buffer_) {
+ buffer_ = MakeUnique<Buffer>(device_, bytes, properties);
+ Result r = buffer_->Initialize(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ if (formats_.empty() || formats_[0].GetComponents().empty())
+ return Result("Vulkan::Formats for VertexBuffer is empty");
+
+ FillVertexBufferWithData(command);
+
+ is_vertex_data_pending_ = false;
+ return {};
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/vertex_buffer.h b/src/vulkan/vertex_buffer.h
new file mode 100644
index 0000000..ef0c242
--- /dev/null
+++ b/src/vulkan/vertex_buffer.h
@@ -0,0 +1,84 @@
+// Copyright 2018 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_VERTEX_BUFFER_H_
+#define SRC_VULKAN_VERTEX_BUFFER_H_
+
+#include <memory>
+#include <vector>
+
+#include "amber/result.h"
+#include "src/format.h"
+#include "src/value.h"
+#include "src/vulkan/buffer.h"
+#include "vulkan/vulkan.h"
+
+namespace amber {
+namespace vulkan {
+
+class VertexBuffer {
+ public:
+ explicit VertexBuffer(VkDevice device);
+ ~VertexBuffer();
+
+ Result SendVertexData(VkCommandBuffer command,
+ const VkPhysicalDeviceMemoryProperties& properties);
+ bool VertexDataSent() { return !is_vertex_data_pending_; }
+
+ void SetData(uint8_t location,
+ const Format& format,
+ const std::vector<Value>& values);
+
+ const std::vector<VkVertexInputAttributeDescription>& GetVertexInputAttr()
+ const {
+ return vertex_attr_desc_;
+ }
+
+ VkVertexInputBindingDescription GetVertexInputBinding() const {
+ VkVertexInputBindingDescription vertex_binding_desc = {};
+ vertex_binding_desc.binding = 0;
+ vertex_binding_desc.stride = stride_in_bytes_;
+ vertex_binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+ return vertex_binding_desc;
+ }
+
+ size_t GetVertexCount() const {
+ if (data_.empty())
+ return 0;
+
+ return data_[0].size() / formats_[0].GetComponents().size();
+ }
+
+ void BindToCommandBuffer(VkCommandBuffer command);
+
+ private:
+ void FillVertexBufferWithData(VkCommandBuffer command);
+
+ VkDevice device_ = VK_NULL_HANDLE;
+
+ bool is_vertex_data_pending_ = true;
+
+ std::unique_ptr<Buffer> buffer_;
+ uint32_t stride_in_bytes_ = 0;
+
+ std::vector<Format> formats_;
+ std::vector<std::vector<Value>> data_;
+
+ std::vector<VkVertexInputAttributeDescription> vertex_attr_desc_;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_VERTEX_BUFFER_H_