Browse Source

Initial commit

master
Brian Ho 6 years ago
commit
9e72bd3fb1
9 changed files with 1002 additions and 0 deletions
  1. 9
    0
      .gitignore
  2. 50
    0
      Android.mk
  3. 7
    0
      Application.mk
  4. 130
    0
      Makefile
  5. 769
    0
      tess.cc
  6. 8
    0
      tess.frag
  7. 14
    0
      tess.tesc
  8. 10
    0
      tess.tese
  9. 5
    0
      tess.vert

+ 9
- 0
.gitignore View File

@@ -0,0 +1,9 @@
tess
tess.png
tess.vert.spv
tess.tesc.spv
tess.tese.spv
tess.frag.spv
*.rd
libs/
obj/

+ 50
- 0
Android.mk View File

@@ -0,0 +1,50 @@
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
PNG_DIR = $(COMPILE_ROOT)/external/libpng/src
PNG_SRC_FILES = $(PNG_DIR)/png.c \
$(PNG_DIR)/pngerror.c \
$(PNG_DIR)/pngget.c \
$(PNG_DIR)/pngmem.c \
$(PNG_DIR)/pngpread.c \
$(PNG_DIR)/pngread.c \
$(PNG_DIR)/pngrio.c \
$(PNG_DIR)/pngrtran.c \
$(PNG_DIR)/pngrutil.c \
$(PNG_DIR)/pngset.c \
$(PNG_DIR)/pngtrans.c \
$(PNG_DIR)/pngwio.c \
$(PNG_DIR)/pngwrite.c \
$(PNG_DIR)/pngwtran.c \
$(PNG_DIR)/pngwutil.c \
$(PNG_DIR)/arm/arm_init.c \
$(PNG_DIR)/arm/filter_neon_intrinsics.c

ZLIB_DIR = $(COMPILE_ROOT)/external/zlib/src
ZLIB_SRC_FILES = \
$(ZLIB_DIR)/adler32.c \
$(ZLIB_DIR)/compress.c \
$(ZLIB_DIR)/crc32.c \
$(ZLIB_DIR)/deflate.c \
$(ZLIB_DIR)/gzclose.c \
$(ZLIB_DIR)/gzlib.c \
$(ZLIB_DIR)/gzread.c \
$(ZLIB_DIR)/gzwrite.c \
$(ZLIB_DIR)/infback.c \
$(ZLIB_DIR)/inflate.c \
$(ZLIB_DIR)/inftrees.c \
$(ZLIB_DIR)/inffast.c \
$(ZLIB_DIR)/trees.c \
$(ZLIB_DIR)/uncompr.c \
$(ZLIB_DIR)/zutil.c

NAME := $(TARGET_NAME)
LOCAL_MODULE := $(NAME)
LOCAL_SRC_FILES := $(PNG_SRC_FILES) $(ZLIB_SRC_FILES) $(NAME).cc
LOCAL_C_INCLUDES := $(COMPILE_ROOT)/include

LOCAL_CPP_INCLUDES := $(COMPILE_ROOT)/include $(COMPILE_ROOT)/external
LOCAL_CFLAGS := -Wall
LOCAL_CPPFLAGS = -Wall -stdlib=libc++
LOCAL_LDLIBS := -lvulkan
include $(BUILD_EXECUTABLE)

+ 7
- 0
Application.mk View File

@@ -0,0 +1,7 @@
APP_OPTIM := debug
APP_ABI := arm64-v8a
APP_STL := c++_static
APP_CPPFLAGS := -frtti -fexceptions -std=c++14
APP_PLATFORM := android-24
APP_BUILD_SCRIPT := Android.mk
NDK_TOOLCHAIN_VERSION := clang

+ 130
- 0
Makefile View File

@@ -0,0 +1,130 @@
SUBDIR = tess
NAME = tess
CFLAGS = -std=c++17 --sysroot="$(SYSROOT)" -Wall
LDFLAGS = -lvulkan -lpng

TRACE = False
ifeq ($(target),$(filter $(target), cheza cheza-trace))
CXX = /usr/bin/armv7a-cros-linux-gnueabihf-clang++
CC = /usr/bin/armv7a-cros-linux-gnueabihf-clang
SYSROOT = /build/cheza
SSH_DUT = cheza
TARGET = cheza
ifeq ($(target), cheza-trace)
TRACE = True
endif
else ifeq ($(target), atlas)
CXX = /usr/bin/x86_64-cros-linux-gnu-clang++
CC = /usr/bin/x86_64-cros-linux-gnu-clang
SYSROOT = /build/atlas
SSH_DUT = atlas
TARGET = atlas
else ifeq ($(target), android)
ANDROID_ROOT = ~/android
SSH_DUT = android
TARGET = android
else ifeq ($(target), android-trace)
ANDROID_ROOT = ~/android
SSH_DUT = android
TRACE = True
TARGET = android
else ifeq ($(target), local)
CXX = clang++
CC = clang
TARGET = local
else
CXX = INVALID
CC = INVALID
TARGET = INVALID
endif

all: shaders build deploy run

build: check
@echo Building...
ifeq ($(TARGET), android)
@NDK_PROJECT_PATH=. \
NDK_ROOT=${ANDROID_ROOT}/ndk/ \
TARGET_NAME=${NAME} \
COMPILE_ROOT=${ANDROID_ROOT}/compile \
${ANDROID_ROOT}/ndk/ndk-build NDK_APPLICATION_MK=./Application.mk
else
@$(CXX) $(CFLAGS) -o ${NAME} ${NAME}.cc $(LDFLAGS)
endif

deploy: check
@echo Deploying to $(SSH_DUT)...
ifeq ($(TARGET), android)
@adb push ${NAME}.vert.spv /data/vulkan/${SUBDIR}/${NAME}.vert.spv
@adb push ${NAME}.tesc.spv /data/vulkan/${SUBDIR}/${NAME}.tesc.spv
@adb push ${NAME}.tese.spv /data/vulkan/${SUBDIR}/${NAME}.tese.spv
@adb push ${NAME}.frag.spv /data/vulkan/${SUBDIR}/${NAME}.frag.spv
@adb push libs/arm64-v8a/${NAME} /data/vulkan/${SUBDIR}/
else ifneq ($(TARGET), local)
@scp ${NAME}.vert.spv $(SSH_DUT):~/${SUBDIR}/${NAME}.vert.spv
@scp ${NAME}.tesc.spv $(SSH_DUT):~/${SUBDIR}/${NAME}.tesc.spv
@scp ${NAME}.tese.spv $(SSH_DUT):~/${SUBDIR}/${NAME}.tese.spv
@scp ${NAME}.frag.spv $(SSH_DUT):~/${SUBDIR}/${NAME}.frag.spv
@scp ${NAME} $(SSH_DUT):~/${SUBDIR}/
endif

run: check
ifeq ($(TARGET), android)
@echo Running on $(SSH_DUT)...
ifeq ($(TRACE), True)
@adb shell "cd /data/vulkan/${SUBDIR} && LD_PRELOAD=/data/vulkan/libwrap.so ./${NAME}"
@echo Copying artifacts back to local device...
@adb pull /data/vulkan/${SUBDIR}/${NAME}.png .
@adb pull /sdcard/trace.rd .
else
@adb shell "cd /data/vulkan/${SUBDIR} && ./${NAME}"
@echo Copying artifacts back to local device...
@adb pull /data/vulkan/${SUBDIR}/${NAME}.png .
endif
else ifneq ($(TARGET), local)
@echo Running on $(SSH_DUT)...
ifeq ($(TRACE), True)
@ssh -tt $(SSH_DUT) "~/trace.sh 'cd ~/${SUBDIR} && ./${NAME}'"
@echo Copying artifacts back to local device...
@scp $(SSH_DUT):~/${SUBDIR}/${NAME}.png .
@scp $(SSH_DUT):/tmp/trace.rd .
else
@ssh -tt $(SSH_DUT) 'cd ~/${SUBDIR} && ./${NAME}'
@echo Copying artifacts back to local device...
@scp $(SSH_DUT):~/${SUBDIR}/${NAME}.png .
endif
else
@echo Running locally...
@./$(NAME)
endif

shaders:
@glslc -c ${NAME}.vert
@glslc -c ${NAME}.tesc
@glslc -c ${NAME}.tese
@glslc -c ${NAME}.frag

clean: check
@rm -f ${NAME}
@rm -f ${NAME}.png
@rm -f ${NAME}.vert.spv
@rm -f ${NAME}.tesc.spv
@rm -f ${NAME}.tese.spv
@rm -f ${NAME}.frag.spv
@rm -f trace.rd
@rm -rf libs
@rm -rf obj
ifneq ($(TARGET), local)
ifeq ($(TARGET), android)
@adb shell rm /data/vulkan/${SUBDIR}/*
else
@ssh $(SSH_DUT) 'rm -f ~/${SUBDIR}/${NAME} ~/${SUBDIR}/${NAME}.vert.spv \
~/${SUBDIR}/${NAME}.tesc.spv ~/${SUBDIR}/${NAME}.tese.spv \
~/${SUBDIR}/${NAME}.frag.spv ~/${SUBDIR}/${NAME}.png'
endif
endif

check:
ifeq ($(TARGET), INVALID)
$(error $$target must be one of [atlas, cheza, cheza-trace, android, android-trace, local])
endif

+ 769
- 0
tess.cc View File

@@ -0,0 +1,769 @@
#include <png.h>
#include <vulkan/vulkan.h>

#include <array>
#include <cstring>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

const uint32_t kWidth = 256;
const uint32_t kHeight = 256;
const VkFormat kVulkanFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
const std::string kVertexShaderPath = "tess.vert.spv";
const std::string kTessControlShaderPath = "tess.tesc.spv";
const std::string kTessEvalShaderPath = "tess.tese.spv";
const std::string kFragmentShaderPath = "tess.frag.spv";

#define ERROR(message) \
std::cerr << message << std::endl; \
std::exit(EXIT_FAILURE)

struct Vertex {
float position[3];
float color[3];
};

VkInstance CreateVkInstance() {
VkApplicationInfo app_info = {};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
app_info.apiVersion = VK_API_VERSION_1_1;

VkInstanceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.pApplicationInfo = &app_info;
create_info.enabledExtensionCount = 0;
create_info.ppEnabledExtensionNames = nullptr;
create_info.enabledLayerCount = 0;
create_info.ppEnabledLayerNames = nullptr;

VkInstance instance;
VkResult result = vkCreateInstance(&create_info, nullptr, &instance);
if (result != VK_SUCCESS) {
ERROR("Error creating VkInstance: " << result);
}
return instance;
}

VkPhysicalDevice ChooseVkPhysicalDevice(VkInstance instance) {
uint32_t device_count = 0;
VkResult result = vkEnumeratePhysicalDevices(instance, &device_count, nullptr);
if (result != VK_SUCCESS) {
ERROR("Error enumerating VkPhysicalDevices: " << result);
}
if (device_count == 0) {
ERROR("No available VkPhysicalDevices");
}

std::vector<VkPhysicalDevice> devices(device_count);
result = vkEnumeratePhysicalDevices(instance, &device_count, devices.data());
if (result != VK_SUCCESS) {
ERROR("Error fetching VkPhysicalDevices: " << result);
}

std::cout << "Found " << device_count << " device(s):" << std::endl;
VkPhysicalDevice chosen_device = VK_NULL_HANDLE;
for (VkPhysicalDevice device : devices) {
VkPhysicalDeviceProperties device_props;
vkGetPhysicalDeviceProperties(device, &device_props);
std::cout << "\t- " << device_props.deviceName << " [V: " <<
VK_VERSION_MAJOR(device_props.apiVersion) << "." <<
VK_VERSION_MINOR(device_props.apiVersion) << "." <<
VK_VERSION_PATCH(device_props.apiVersion) << "]" << std::endl;
// Currently, any device with Vulkan API version 1.1 is fine.
if (chosen_device == VK_NULL_HANDLE && device_props.apiVersion >= VK_API_VERSION_1_1) {
chosen_device = device;
}
}

if (chosen_device == VK_NULL_HANDLE) {
ERROR("Unable to find suitable VkPhysicalDevice");
}
return chosen_device;
}

uint32_t ChooseDeviceQueueFamilyIndex(VkPhysicalDevice physical_device) {
uint32_t props_count;
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &props_count, nullptr);
std::vector<VkQueueFamilyProperties> props(props_count);
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &props_count, props.data());

// Simply choose the first graphics queue.
for (size_t i = 0; i < props_count; i++) {
const VkQueueFamilyProperties& prop = props[i];
if ((prop.queueFlags & VK_QUEUE_GRAPHICS_BIT) && prop.queueCount > 0) {
return i;
}
}

ERROR("Unable to find suitable queue family");
}

VkDevice CreateVkDevice(VkPhysicalDevice physical_device, uint32_t device_queue_family_index) {
VkDeviceQueueCreateInfo queue_create_info = {};
queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info.queueFamilyIndex = device_queue_family_index;
queue_create_info.queueCount = 1;
float queue_priority = 1.0f;
queue_create_info.pQueuePriorities = &queue_priority;

VkPhysicalDeviceFeatures physical_device_features = {};
physical_device_features.tessellationShader = true;

VkDeviceCreateInfo device_create_info = {};
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_create_info.queueCreateInfoCount = 1;
device_create_info.pQueueCreateInfos = &queue_create_info;
// Let's not use any device extensions for now.
device_create_info.enabledExtensionCount = 0;
device_create_info.ppEnabledExtensionNames = nullptr;
device_create_info.pEnabledFeatures = &physical_device_features;

VkDevice device;
VkResult result = vkCreateDevice(physical_device, &device_create_info, nullptr, &device);
if (result != VK_SUCCESS) {
ERROR("Unable to create logical device: " << result);
}
return device;
}

VkQueue GetVkQueue(VkDevice device, uint32_t device_queue_family_index) {
VkQueue queue;
vkGetDeviceQueue(device, device_queue_family_index, /*queueIndex*/ 0, &queue);
return queue;
}

VkCommandPool CreateVkCommandPool(VkDevice device, uint32_t device_queue_family_index) {
VkCommandPool command_pool;
VkCommandPoolCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
create_info.flags = 0;
create_info.queueFamilyIndex = device_queue_family_index;
VkResult result = vkCreateCommandPool(device, &create_info, nullptr, &command_pool);
if (result != VK_SUCCESS) {
ERROR("Unable to create command pool: " << result);
}
return command_pool;
}

uint32_t FindMemoryType(uint32_t valid_image_memory_types,
VkPhysicalDeviceMemoryProperties device_memory_properties,
VkMemoryPropertyFlags memory_property_flags) {
for (uint32_t i = 0; i < device_memory_properties.memoryTypeCount; i++) {
// We don't care about performance, so just choose the first mappable memory type.
if ((valid_image_memory_types & (1 << i)) &&
((device_memory_properties.memoryTypes[i].propertyFlags & memory_property_flags) ==
memory_property_flags)) {
return i;
}
}
ERROR("Unable to find suitable memory type index");
}

VkBuffer CreateVertexBuffer(VkPhysicalDevice physical_device, VkDevice device) {
std::array<Vertex, 3> vertices = {};
vertices[0].position[0] = 0.0;
vertices[0].position[1] = -0.5;
vertices[0].position[2] = 0.0;
vertices[0].color[0] = 1.0;
vertices[0].color[1] = 0.0;
vertices[0].color[2] = 0.0;

vertices[1].position[0] = 0.5;
vertices[1].position[1] = 0.5;
vertices[1].position[2] = 0.0;
vertices[1].color[0] = 0.0;
vertices[1].color[1] = 1.0;
vertices[1].color[2] = 0.0;

vertices[2].position[0] = -0.5;
vertices[2].position[1] = 0.5;
vertices[2].position[2] = 0.0;
vertices[2].color[0] = 0.0;
vertices[2].color[1] = 0.0;
vertices[2].color[2] = 1.0;

VkBufferCreateInfo buffer_info = {};
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_info.size = sizeof(vertices);
buffer_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buffer_info.flags = 0;

VkBuffer buffer;
VkResult result = vkCreateBuffer(device, &buffer_info, nullptr, &buffer);
if (result != VK_SUCCESS) {
ERROR("Unable to create VkBuffer: " << result);
}

VkPhysicalDeviceMemoryProperties device_memory_properties;
vkGetPhysicalDeviceMemoryProperties(physical_device, &device_memory_properties);

VkMemoryRequirements buffer_memory_requirements;
vkGetBufferMemoryRequirements(device, buffer, &buffer_memory_requirements);

VkMemoryAllocateInfo allocate_info = {};
allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocate_info.allocationSize = buffer_memory_requirements.size;
allocate_info.memoryTypeIndex = FindMemoryType(
buffer_memory_requirements.memoryTypeBits,
device_memory_properties,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);

VkDeviceMemory memory;
result = vkAllocateMemory(device, &allocate_info, nullptr, &memory);
if (result != VK_SUCCESS) {
ERROR("Unable to allocate memory: " << result);
}

vkBindBufferMemory(device, buffer, memory, 0);
void* data = nullptr;
vkMapMemory(device, memory, 0, buffer_info.size, 0, &data);
memcpy(data, vertices.data(), buffer_info.size);
vkUnmapMemory(device, memory);

return buffer;
}

VkCommandBuffer CreateVkCommandBuffer(VkDevice device, VkCommandPool command_pool) {
VkCommandBufferAllocateInfo allocate_info = {};
allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocate_info.commandPool = command_pool;
allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocate_info.commandBufferCount = 1;

VkCommandBuffer command_buffer;
VkResult result = vkAllocateCommandBuffers( device, &allocate_info, &command_buffer);
if (result != VK_SUCCESS) {
ERROR("Unable to create VkCommandBuffer: " << result);
}
return command_buffer;
}

VkRenderPass CreateVkRenderPass(VkDevice device) {
VkAttachmentDescription attachment_description = {};
attachment_description.format = kVulkanFormat;
attachment_description.samples = VK_SAMPLE_COUNT_1_BIT;
attachment_description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment_description.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment_description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment_description.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;

VkAttachmentReference attachment_reference = {};
attachment_reference.attachment = 0;
attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

VkSubpassDescription subpass_description = {};
subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass_description.colorAttachmentCount = 1;
subpass_description.pColorAttachments = &attachment_reference;

VkSubpassDependency subpass_dependency = {};
subpass_dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
subpass_dependency.dstSubpass = 0;
subpass_dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
subpass_dependency.srcAccessMask = 0;
subpass_dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
subpass_dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;

VkRenderPassCreateInfo render_pass_info = {};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.attachmentCount = 1;
render_pass_info.pAttachments = &attachment_description;
render_pass_info.subpassCount = 1;
render_pass_info.pSubpasses = &subpass_description;
render_pass_info.dependencyCount = 1;
render_pass_info.pDependencies = &subpass_dependency;

VkRenderPass render_pass;
VkResult result = vkCreateRenderPass(device, &render_pass_info, nullptr, &render_pass);
if (result != VK_SUCCESS) {
ERROR("Unable to create render pass: " << result);
}
return render_pass;
}

VkShaderModule CreateVkShaderModule(VkDevice device, std::string path) {
std::ifstream fstream(path);
if (!fstream) {
ERROR("Unable to open: " << path);
}
std::stringstream buffer;
buffer << fstream.rdbuf();
std::string spirv_source = buffer.str();

VkShaderModuleCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
create_info.codeSize = spirv_source.length();
create_info.pCode = (const uint32_t*)spirv_source.c_str();

VkShaderModule shader_module;
VkResult result = vkCreateShaderModule(device, &create_info, nullptr, &shader_module);
if (result != VK_SUCCESS) {
ERROR("Unable to create shader module for " << path << ": ");
}
return shader_module;
}

VkPipeline CreateVkPipeline(VkDevice device, VkRenderPass render_pass) {
VkShaderModule vertex_shader_module = CreateVkShaderModule(device, kVertexShaderPath);
VkShaderModule tess_control_shader_module = CreateVkShaderModule(device, kTessControlShaderPath);
VkShaderModule tess_eval_shader_module = CreateVkShaderModule(device, kTessEvalShaderPath);
VkShaderModule fragment_shader_module = CreateVkShaderModule(device, kFragmentShaderPath);

VkPipelineShaderStageCreateInfo vertex_shader_stage_info = {};
vertex_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertex_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertex_shader_stage_info.module = vertex_shader_module;
vertex_shader_stage_info.pName = "main";

VkPipelineShaderStageCreateInfo tess_control_shader_stage_info = {};
tess_control_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
tess_control_shader_stage_info.stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
tess_control_shader_stage_info.module = tess_control_shader_module;
tess_control_shader_stage_info.pName = "main";

VkPipelineShaderStageCreateInfo tess_eval_shader_stage_info = {};
tess_eval_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
tess_eval_shader_stage_info.stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
tess_eval_shader_stage_info.module = tess_eval_shader_module;
tess_eval_shader_stage_info.pName = "main";

VkPipelineShaderStageCreateInfo fragment_shader_stage_info = {};
fragment_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragment_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragment_shader_stage_info.module = fragment_shader_module;
fragment_shader_stage_info.pName = "main";

std::vector<VkPipelineShaderStageCreateInfo> shader_stages =
{ vertex_shader_stage_info, tess_control_shader_stage_info, tess_eval_shader_stage_info,
fragment_shader_stage_info };

VkVertexInputBindingDescription binding_description = {};
binding_description.binding = 0;
binding_description.stride = sizeof(Vertex);
binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;

std::array<VkVertexInputAttributeDescription, 2> attribute_descriptions = {};
attribute_descriptions[0].binding = 0;
attribute_descriptions[0].location = 0;
attribute_descriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
attribute_descriptions[0].offset = offsetof(Vertex, position);

attribute_descriptions[1].binding = 0;
attribute_descriptions[1].location = 1;
attribute_descriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
attribute_descriptions[1].offset = offsetof(Vertex, color);

VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_info.vertexBindingDescriptionCount = 1;
vertex_input_info.pVertexBindingDescriptions = &binding_description;
vertex_input_info.vertexAttributeDescriptionCount = attribute_descriptions.size();
vertex_input_info.pVertexAttributeDescriptions = attribute_descriptions.data();

VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {};
input_assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
input_assembly_info.primitiveRestartEnable = VK_FALSE;

VkPipelineTessellationStateCreateInfo tessellation_state_info = {};
tessellation_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
tessellation_state_info.pNext = nullptr;
tessellation_state_info.flags = 0;
tessellation_state_info.patchControlPoints = 3;

VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)kWidth;
viewport.height = (float)kHeight;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;

VkExtent2D extent = {};
extent.width = kWidth;
extent.height = kHeight;
VkRect2D scissor = {};
scissor.offset = {0, 0};
scissor.extent = extent;

VkPipelineViewportStateCreateInfo viewport_state_info = {};
viewport_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state_info.viewportCount = 1;
viewport_state_info.pViewports = &viewport;
viewport_state_info.scissorCount = 1;
viewport_state_info.pScissors = &scissor;

VkPipelineRasterizationStateCreateInfo rasterization_state_info = {};
rasterization_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterization_state_info.depthClampEnable = VK_FALSE;
rasterization_state_info.rasterizerDiscardEnable = VK_FALSE;
rasterization_state_info.polygonMode = VK_POLYGON_MODE_FILL;
rasterization_state_info.lineWidth = 1.0f;
rasterization_state_info.cullMode = VK_CULL_MODE_BACK_BIT;
rasterization_state_info.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterization_state_info.depthBiasEnable = VK_FALSE;

VkPipelineMultisampleStateCreateInfo multisampling_state_info = {};
multisampling_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling_state_info.sampleShadingEnable = VK_FALSE;
multisampling_state_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;

VkPipelineColorBlendAttachmentState color_blend_attachment_state = {};
color_blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
color_blend_attachment_state.blendEnable = VK_FALSE;

VkPipelineColorBlendStateCreateInfo color_blend_state = {};
color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
color_blend_state.logicOpEnable = VK_FALSE;
color_blend_state.attachmentCount = 1;
color_blend_state.pAttachments = &color_blend_attachment_state;

VkPipelineLayoutCreateInfo pipeline_layout_create_info = {};
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;

VkPipelineLayout pipeline_layout;
VkResult result = vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr,
&pipeline_layout);
if (result != VK_SUCCESS) {
ERROR("Unable to create VkPipelineLayout: " << result);
}

VkGraphicsPipelineCreateInfo pipeline_create_info = {};
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_create_info.stageCount = shader_stages.size();
pipeline_create_info.pStages = shader_stages.data();
pipeline_create_info.pVertexInputState = &vertex_input_info;
pipeline_create_info.pInputAssemblyState = &input_assembly_info;
pipeline_create_info.pTessellationState = &tessellation_state_info;
pipeline_create_info.pViewportState = &viewport_state_info;
pipeline_create_info.pRasterizationState = &rasterization_state_info;
pipeline_create_info.pMultisampleState = &multisampling_state_info;
pipeline_create_info.pDepthStencilState = nullptr;
pipeline_create_info.pColorBlendState = &color_blend_state;
pipeline_create_info.pDynamicState = nullptr;
pipeline_create_info.layout = pipeline_layout;
pipeline_create_info.renderPass = render_pass;
pipeline_create_info.subpass = 0;
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
pipeline_create_info.basePipelineIndex = -1;

VkPipeline pipeline;
result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_create_info, nullptr,
&pipeline);
if (result != VK_SUCCESS) {
ERROR("Unable to create VkPipeline: " << result);
}
return pipeline;
}

VkImage CreateVkImage(VkDevice device, VkImageTiling image_tiling, VkImageUsageFlags usage) {
VkImageCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
create_info.pNext = nullptr;
create_info.imageType = VK_IMAGE_TYPE_2D;
create_info.format = kVulkanFormat;
VkExtent3D extent = {};
extent.width = kWidth;
extent.height = kHeight;
extent.depth = 1;
create_info.extent = extent;
create_info.mipLevels = 1;
create_info.arrayLayers = 1;
create_info.samples = VK_SAMPLE_COUNT_1_BIT;
create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
create_info.tiling = image_tiling;
create_info.usage = usage;
create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;

VkImage image;
VkResult result = vkCreateImage(device, &create_info, nullptr, &image);
if (result != VK_SUCCESS) {
ERROR("Unable to create VkImage: " << result);
}
return image;
}

inline VkImage CreateRenderVkImage(VkDevice device) {
return CreateVkImage(device, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
}

inline VkImage CreateScanoutVkImage(VkDevice device) {
return CreateVkImage(device, VK_IMAGE_TILING_LINEAR,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
}

VkDeviceMemory AllocateAndBindMemory(VkPhysicalDevice physical_device, VkDevice device,
VkImage image, VkMemoryPropertyFlags memory_property_flags) {
VkMemoryRequirements image_memory_requirements;
vkGetImageMemoryRequirements(device, image, &image_memory_requirements);

VkPhysicalDeviceMemoryProperties device_memory_properties;
vkGetPhysicalDeviceMemoryProperties(physical_device, &device_memory_properties);

VkMemoryAllocateInfo memory_allocate_info = {};
memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memory_allocate_info.allocationSize = image_memory_requirements.size;
memory_allocate_info.memoryTypeIndex = FindMemoryType(
image_memory_requirements.memoryTypeBits, device_memory_properties,
memory_property_flags);

VkDeviceMemory image_memory;
VkResult result = vkAllocateMemory(device, &memory_allocate_info, nullptr, &image_memory);
if (result != VK_SUCCESS) {
ERROR("Unable to allocate image memory: " << result);
}

result = vkBindImageMemory(device, image, image_memory, 0);
if (result != VK_SUCCESS) {
ERROR("Unable to bind image memory: " << result);
}
return image_memory;
}

inline VkDeviceMemory AllocateAndBindRenderMemory(VkPhysicalDevice physical_device,
VkDevice device, VkImage image) {
return AllocateAndBindMemory(physical_device, device, image, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}

inline VkDeviceMemory AllocateAndBindScanoutMemory(VkPhysicalDevice physical_device,
VkDevice device, VkImage image) {
return AllocateAndBindMemory(physical_device, device, image, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
}

VkImageView CreateVkImageView(VkDevice device, VkImage image) {
VkImageViewCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
create_info.image = image;
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
create_info.format = kVulkanFormat;

VkComponentMapping component_mapping = {};
component_mapping.r = VK_COMPONENT_SWIZZLE_IDENTITY;
component_mapping.b = VK_COMPONENT_SWIZZLE_IDENTITY;
component_mapping.g = VK_COMPONENT_SWIZZLE_IDENTITY;
component_mapping.a = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.components = component_mapping;

VkImageSubresourceRange subresource_range = {};
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource_range.baseMipLevel = 0;
subresource_range.levelCount = 1;
subresource_range.baseArrayLayer = 0;
subresource_range.layerCount = 1;
create_info.subresourceRange = subresource_range;

VkImageView image_view;
VkResult result = vkCreateImageView(device, &create_info, nullptr, &image_view);
if (result != VK_SUCCESS) {
ERROR("Unable to create VkImageView: " << result);
}
return image_view;
}

VkFramebuffer CreateVkFramebuffer(VkDevice device, VkRenderPass render_pass,
VkImageView image_view) {
VkFramebufferCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
create_info.renderPass = render_pass;
create_info.attachmentCount = 1;
create_info.pAttachments = &image_view;
create_info.width = kWidth;
create_info.height = kHeight;
create_info.layers = 1;

VkFramebuffer framebuffer;
VkResult result = vkCreateFramebuffer(device, &create_info, nullptr, &framebuffer);
if (result != VK_SUCCESS) {
ERROR("Unable to create VkFramebuffer: " << result);
}
return framebuffer;
}

void BeginCommandBuffer(VkCommandBuffer command_buffer) {
VkCommandBufferBeginInfo command_buffer_begin_info = {};
command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
command_buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
VkResult result = vkBeginCommandBuffer(command_buffer, &command_buffer_begin_info);
if (result != VK_SUCCESS) {
ERROR("Unable to begin command buffer recording: " << result);
}
}

void EndCommandBufferAndSubmit(VkCommandBuffer command_buffer, VkQueue queue) {
VkResult result = vkEndCommandBuffer(command_buffer);
if (result != VK_SUCCESS) {
ERROR("Unable to end command buffer recording: " << result);
}

VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer;
result = vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
if (result != VK_SUCCESS) {
ERROR("Error in submitting command buffer to queue: " << result);
}
}

void WaitForIdle(VkQueue queue) {
// In a real application, we should use real synchronization primitives to figure out when the
// command buffers have been executed. Waiting on an idle queue is simple, but it can cause
// deadlock if we continue to submit to the queue while we wait for idle.
VkResult result = vkQueueWaitIdle(queue);
if (result != VK_SUCCESS) {
ERROR("Error in waiting for graphics queue to reach idle state: " << result);
}
}

void Draw(VkDevice device, VkQueue queue, VkBuffer vertex_buffer, VkRenderPass render_pass,
VkPipeline pipeline, VkFramebuffer framebuffer, VkCommandBuffer command_buffer) {
BeginCommandBuffer(command_buffer);
VkRenderPassBeginInfo render_pass_begin_info = {};
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_begin_info.renderPass = render_pass;
render_pass_begin_info.framebuffer = framebuffer;
VkRect2D render_area = {};
render_area.offset = { 0, 0 };
render_area.extent = { kWidth, kHeight };
render_pass_begin_info.renderArea = render_area;
render_pass_begin_info.clearValueCount = 1;
VkClearValue clear_value = {};
clear_value.color.float32[0] = 0.0f;
clear_value.color.float32[1] = 0.0f;
clear_value.color.float32[2] = 0.0f;
clear_value.color.float32[3] = 1.0f;
render_pass_begin_info.pClearValues = &clear_value;
vkCmdBeginRenderPass(command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);

vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
VkDeviceSize offset = 0;
vkCmdBindVertexBuffers(command_buffer, 0, 1, &vertex_buffer, &offset);
vkCmdDraw(command_buffer, 3, 1, 0, 0);
vkCmdEndRenderPass(command_buffer);
}

void BlitToScanoutImage(VkQueue queue, VkCommandBuffer command_buffer, VkImage source_image,
VkImage dest_image) {
// We need to make sure the writes of the render pass have executed before actually scanning out
// the rendered image. Also, we need to transition the layout of the scanout image to
// VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL.
VkImageMemoryBarrier image_memory_barrier = {};
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
image_memory_barrier.pNext = nullptr;
image_memory_barrier.srcAccessMask = 0;
image_memory_barrier.dstAccessMask = 0;
image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.image = dest_image;
VkImageSubresourceRange subresource_range = {};
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource_range.baseMipLevel = 0;
subresource_range.levelCount = 1;
subresource_range.baseArrayLayer = 0;
subresource_range.layerCount = 1;
image_memory_barrier.subresourceRange = subresource_range;
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);

VkOffset3D blit_start = {};
blit_start.x = 0;
blit_start.y = 0;
blit_start.z = 0;
VkOffset3D blit_end = {};
blit_end.x = kWidth;
blit_end.y = kHeight;
blit_end.z = 1;
VkImageSubresourceLayers subresource_layers = {};
subresource_layers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource_layers.mipLevel = 0;
subresource_layers.baseArrayLayer = 0;
subresource_layers.layerCount = 1;
VkImageBlit image_blit = {};
image_blit.srcSubresource = subresource_layers;
image_blit.srcOffsets[0] = blit_start;
image_blit.srcOffsets[1] = blit_end;
image_blit.dstSubresource = subresource_layers;
image_blit.dstOffsets[0] = blit_start;
image_blit.dstOffsets[1] = blit_end;
// TODO(brkho): Support multi-planar formats.
vkCmdBlitImage(command_buffer, source_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dest_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_blit, VK_FILTER_NEAREST);

EndCommandBufferAndSubmit(command_buffer, queue);
}

uint8_t* MapVkDeviceMemory(VkDevice device, VkDeviceMemory image_memory) {
void* mapped_memory = nullptr;
VkResult result = vkMapMemory(device, image_memory, 0, kWidth * kHeight * 4, 0, &mapped_memory);
if (result != VK_SUCCESS) {
ERROR("Unable to map device memory: " << result);
}
return static_cast<uint8_t*>(mapped_memory);
}

void WriteImage(uint8_t* pixels) {
FILE *f = fopen("tess.png", "wb");

png_image image_info = {};
image_info.version = PNG_IMAGE_VERSION;
image_info.width = kWidth;
image_info.height = kHeight;
image_info.format = PNG_FORMAT_RGBA;
if (png_image_write_to_stdio(&image_info, f, 0, pixels, kWidth * 4, nullptr) == 0) {
ERROR("Error writing PNG: " << image_info.message);
}

fclose(f);
}

void Cleanup(VkInstance instance, VkDevice device) {
vkDestroyDevice(device, nullptr);
vkDestroyInstance(instance, nullptr);
}

int main() {
VkInstance instance = CreateVkInstance();
VkPhysicalDevice physical_device = ChooseVkPhysicalDevice(instance);
uint32_t device_queue_family_index = ChooseDeviceQueueFamilyIndex(physical_device);
VkDevice device = CreateVkDevice(physical_device, device_queue_family_index);
VkQueue queue = GetVkQueue(device, device_queue_family_index);
VkCommandPool command_pool = CreateVkCommandPool(device, device_queue_family_index);
VkBuffer vertex_buffer = CreateVertexBuffer(physical_device, device);
VkCommandBuffer command_buffer = CreateVkCommandBuffer(device, command_pool);

VkRenderPass render_pass = CreateVkRenderPass(device);
VkPipeline pipeline = CreateVkPipeline(device, render_pass);

VkImage render_image = CreateRenderVkImage(device);
AllocateAndBindRenderMemory(physical_device, device, render_image);
VkImageView render_image_view = CreateVkImageView(device, render_image);
VkFramebuffer framebuffer = CreateVkFramebuffer(device, render_pass, render_image_view);

VkImage scanout_image = CreateScanoutVkImage(device);
VkDeviceMemory scanout_image_memory =
AllocateAndBindScanoutMemory(physical_device, device, scanout_image);

Draw(device, queue, vertex_buffer, render_pass, pipeline, framebuffer, command_buffer);
// Since the render target is created with VK_IMAGE_TILING_OPTIMAL, we need to copy it to a linear
// format before scanning out.
BlitToScanoutImage(queue, command_buffer, render_image, scanout_image);
WaitForIdle(queue);

uint8_t* pixels = MapVkDeviceMemory(device, scanout_image_memory);
WriteImage(pixels);

Cleanup(instance, device);

return EXIT_SUCCESS;
}

+ 8
- 0
tess.frag View File

@@ -0,0 +1,8 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(location = 0) out vec4 outColor;

void main() {
outColor = vec4(1.0, 0.0, 0.0, 1.0);
}

+ 14
- 0
tess.tesc View File

@@ -0,0 +1,14 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable

layout (vertices = 3) out;

void main() {
gl_TessLevelOuter[0] = 4.0;
gl_TessLevelOuter[1] = 4.0;
gl_TessLevelOuter[2] = 4.0;
gl_TessLevelOuter[3] = 4.0;

gl_TessLevelInner[0] = 4.0;
gl_TessLevelInner[1] = 4.0;
}

+ 10
- 0
tess.tese View File

@@ -0,0 +1,10 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(quads, equal_spacing, ccw, point_mode) in;

void main() {
float x = (gl_TessCoord.x - 0.5) * 2.0;
float y = (gl_TessCoord.y - 0.5) * 2.0;
gl_Position = vec4(x * 0.9, y * 0.9, 0.5, 1.0);
}

+ 5
- 0
tess.vert View File

@@ -0,0 +1,5 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable

void main() {
}

Loading…
Cancel
Save