Vulkan implementation.

This commit is contained in:
Hans-Kristian Arntzen 2016-02-16 20:24:00 +01:00
parent 0d3c30978d
commit adbf9fed16
73 changed files with 16067 additions and 64 deletions

3
.gitignore vendored
View File

@ -62,6 +62,9 @@ menu/driverspzarch.c
/media/shaders_glsl/ /media/shaders_glsl/
/obj-w32/ /obj-w32/
# Ctags
/tags
# Android # Android
/pkg/android/phoenix/obj/ /pkg/android/phoenix/obj/
/pkg/android/phoenix/assets/ /pkg/android/phoenix/assets/

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "deps/glslang/glslang"]
path = deps/glslang/glslang
url = git://github.com/KhronosGroup/glslang.git

View File

@ -60,7 +60,9 @@ endif
endif endif
CFLAGS += -Wall $(OPTIMIZE_FLAG) $(INCLUDE_DIRS) $(DEBUG_FLAG) -I. CFLAGS += -Wall $(OPTIMIZE_FLAG) $(INCLUDE_DIRS) $(DEBUG_FLAG) -I.
CXXFLAGS := $(CFLAGS) -std=c++98 -D__STDC_CONSTANT_MACROS
APPEND_CFLAGS := $(CFLAGS)
CXXFLAGS += $(APPEND_CFLAGS) -std=c++11 -D__STDC_CONSTANT_MACROS
OBJCFLAGS := $(CFLAGS) -D__STDC_CONSTANT_MACROS OBJCFLAGS := $(CFLAGS) -D__STDC_CONSTANT_MACROS
ifeq ($(CXX_BUILD), 1) ifeq ($(CXX_BUILD), 1)
@ -127,7 +129,7 @@ $(OBJDIR)/%.o: %.c config.h config.mk
@$(if $(Q), $(shell echo echo CC $<),) @$(if $(Q), $(shell echo echo CC $<),)
$(Q)$(CC) $(CFLAGS) $(DEFINES) -MMD -c -o $@ $< $(Q)$(CC) $(CFLAGS) $(DEFINES) -MMD -c -o $@ $<
$(OBJDIR)/%.o: %.cpp $(OBJDIR)/%.o: %.cpp config.h config.mk
@mkdir -p $(dir $@) @mkdir -p $(dir $@)
@$(if $(Q), $(shell echo echo CXX $<),) @$(if $(Q), $(shell echo echo CXX $<),)
$(Q)$(CXX) $(CXXFLAGS) $(DEFINES) -MMD -c -o $@ $< $(Q)$(CXX) $(CXXFLAGS) $(DEFINES) -MMD -c -o $@ $<

View File

@ -630,6 +630,9 @@ ifeq ($(HAVE_GL_CONTEXT), 1)
ifeq ($(HAVE_EGL), 1) ifeq ($(HAVE_EGL), 1)
OBJ += gfx/drivers_context/wayland_ctx.o OBJ += gfx/drivers_context/wayland_ctx.o
endif endif
ifeq ($(HAVE_VULKAN), 1)
OBJ += gfx/drivers_context/wayland_ctx_vulkan.o
endif
endif endif
ifeq ($(HAVE_GLES), 1) ifeq ($(HAVE_GLES), 1)
@ -694,6 +697,45 @@ ifeq ($(HAVE_SDL2), 1)
HAVE_SDL = 0 HAVE_SDL = 0
endif endif
ifeq ($(HAVE_VULKAN), 1)
ifeq ($(platform), win)
GLSLANG_PLATFORM := Windows
else
GLSLANG_PLATFORM := Unix
endif
GLSLANG_SOURCES := \
$(wildcard deps/glslang/*.cpp) \
$(wildcard deps/glslang/glslang/SPIRV/*.cpp) \
$(wildcard deps/glslang/glslang/glslang/GenericCodeGen/*.cpp) \
$(wildcard deps/glslang/glslang/OGLCompilersDLL/*.cpp) \
$(wildcard deps/glslang/glslang/glslang/MachineIndependent/*.cpp) \
$(wildcard deps/glslang/glslang/glslang/MachineIndependent/preprocessor/*.cpp) \
$(wildcard deps/glslang/glslang/glslang/OSDependent/$(GLSLANG_PLATFORM)/*.cpp)
DEFINES += \
-Ideps/glslang/glslang/glslang/OSDependent/$(GLSLANG_PLATFORM) \
-Ideps/glslang/glslang \
-Ideps/glslang/glslang/glslang/MachineIndependent \
-Ideps/glslang/glslang/glslang/Public \
-Ideps/glslang/glslang/SPIRV \
-Ideps/glslang
CXXFLAGS += -Wno-switch -Wno-sign-compare -fno-strict-aliasing -Wno-maybe-uninitialized
GLSLANG_OBJ := $(GLSLANG_SOURCES:.cpp=.o)
OBJ += gfx/drivers/vulkan.o \
gfx/common/vulkan_common.o \
gfx/drivers_font/vulkan_raster_font.o \
gfx/drivers_shader/shader_vulkan.o \
gfx/drivers_shader/glslang_util.o \
$(GLSLANG_OBJ)
ifeq ($(HAVE_MENU_COMMON), 1)
OBJ += menu/drivers_display/menu_display_vulkan.o
endif
LIBS += -lvulkan -lstdc++
endif
ifeq ($(HAVE_OMAP), 1) ifeq ($(HAVE_OMAP), 1)
OBJ += gfx/drivers/omap_gfx.o OBJ += gfx/drivers/omap_gfx.o
endif endif

View File

@ -42,6 +42,8 @@
#define COMMAND_EXT_GLSLP 0x0f840c87U #define COMMAND_EXT_GLSLP 0x0f840c87U
#define COMMAND_EXT_CG 0x0059776fU #define COMMAND_EXT_CG 0x0059776fU
#define COMMAND_EXT_CGP 0x0b8865bfU #define COMMAND_EXT_CGP 0x0b8865bfU
#define COMMAND_EXT_SLANG 0x105ce63aU
#define COMMAND_EXT_SLANGP 0x1bf9adeaU
struct rarch_cmd struct rarch_cmd
{ {
@ -234,6 +236,10 @@ static bool cmd_set_shader(const char *arg)
case COMMAND_EXT_CGP: case COMMAND_EXT_CGP:
type = RARCH_SHADER_CG; type = RARCH_SHADER_CG;
break; break;
case COMMAND_EXT_SLANG:
case COMMAND_EXT_SLANGP:
type = RARCH_SHADER_SLANG;
break;
default: default:
return false; return false;
} }

View File

@ -29,6 +29,7 @@
enum enum
{ {
VIDEO_GL = 0, VIDEO_GL = 0,
VIDEO_VULKAN,
VIDEO_XVIDEO, VIDEO_XVIDEO,
VIDEO_SDL, VIDEO_SDL,
VIDEO_SDL2, VIDEO_SDL2,

View File

@ -0,0 +1,65 @@
ifeq ($(platform),)
platform = unix
ifeq ($(shell uname -a),)
platform = win
else ifneq ($(findstring MINGW,$(shell uname -a)),)
platform = win
else ifneq ($(findstring Darwin,$(shell uname -a)),)
platform = osx
arch = intel
ifeq ($(shell uname -p),powerpc)
arch = ppc
endif
else ifneq ($(findstring win,$(shell uname -a)),)
platform = win
endif
endif
# system platform
system_platform = unix
ifeq ($(shell uname -a),)
EXE_EXT = .exe
system_platform = win
else ifneq ($(findstring MINGW,$(shell uname -a)),)
system_platform = win
endif
TARGET_NAME = testvulkan
ifeq ($(platform), unix)
TARGET := $(TARGET_NAME)_libretro.so
fpic := -fPIC
SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined
VULKAN_LIB := -lvulkan
else
CC = gcc
TARGET := $(TARGET_NAME)_libretro.dll
SHARED := -shared -static-libgcc -static-libstdc++ -s -Wl,--version-script=link.T -Wl,--no-undefined
VULKAN_LIB := -lvulkan
CFLAGS += -I..
endif
ifeq ($(DEBUG), 1)
CFLAGS += -O0 -g
else
CFLAGS += -O3
endif
CFLAGS += -std=gnu99
OBJECTS := libretro-test.o
CFLAGS += -Wall -pedantic $(fpic)
LIBS += $(VULKAN_LIB)
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) $(fpic) $(SHARED) $(INCLUDES) -o $@ $(OBJECTS) $(LIBS) -lm $(EXTRA_VULKAN_LIBS)
%.o: %.c
$(CC) -I../../libretro-common/include $(CFLAGS) -c -o $@ $<
clean:
rm -f $(OBJECTS) $(TARGET)
.PHONY: clean

View File

@ -0,0 +1,793 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define VK_PROTOTYPES
#include "../../libretro_vulkan.h"
#include "shaders/triangle.vert.inc"
#include "shaders/triangle.frag.inc"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static struct retro_hw_render_callback hw_render;
static const struct retro_hw_render_interface_vulkan *vulkan;
#define BASE_WIDTH 320
#define BASE_HEIGHT 240
#define MAX_SYNC 8
struct buffer
{
VkBuffer buffer;
VkDeviceMemory memory;
};
struct vulkan_data
{
unsigned index;
unsigned num_swapchain_images;
uint32_t swapchain_mask;
struct buffer vbo;
struct buffer ubo[MAX_SYNC];
VkPhysicalDeviceMemoryProperties memory_properties;
VkPhysicalDeviceProperties gpu_properties;
VkDescriptorSetLayout set_layout;
VkDescriptorPool desc_pool;
VkDescriptorSet desc_set[MAX_SYNC];
VkPipelineCache pipeline_cache;
VkPipelineLayout pipeline_layout;
VkRenderPass render_pass;
VkPipeline pipeline;
VkCommandPool cmd_pool;
struct retro_vulkan_image images[MAX_SYNC];
VkDeviceMemory image_memory[MAX_SYNC];
VkFramebuffer framebuffers[MAX_SYNC];
VkCommandBuffer cmd[MAX_SYNC];
};
static struct vulkan_data vk;
void retro_init(void)
{}
void retro_deinit(void)
{}
unsigned retro_api_version(void)
{
return RETRO_API_VERSION;
}
void retro_set_controller_port_device(unsigned port, unsigned device)
{
(void)port;
(void)device;
}
void retro_get_system_info(struct retro_system_info *info)
{
memset(info, 0, sizeof(*info));
info->library_name = "TestCore Vulkan";
info->library_version = "v1";
info->need_fullpath = false;
info->valid_extensions = NULL; // Anything is fine, we don't care.
}
void retro_get_system_av_info(struct retro_system_av_info *info)
{
info->timing = (struct retro_system_timing) {
.fps = 60.0,
.sample_rate = 30000.0,
};
info->geometry = (struct retro_game_geometry) {
.base_width = BASE_WIDTH,
.base_height = BASE_HEIGHT,
.max_width = BASE_WIDTH,
.max_height = BASE_HEIGHT,
.aspect_ratio = (float)BASE_WIDTH / (float)BASE_HEIGHT,
};
}
static retro_video_refresh_t video_cb;
static retro_audio_sample_t audio_cb;
static retro_audio_sample_batch_t audio_batch_cb;
static retro_environment_t environ_cb;
static retro_input_poll_t input_poll_cb;
static retro_input_state_t input_state_cb;
void retro_set_environment(retro_environment_t cb)
{
environ_cb = cb;
bool no_rom = true;
cb(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME, &no_rom);
}
void retro_set_audio_sample(retro_audio_sample_t cb)
{
audio_cb = cb;
}
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
{
audio_batch_cb = cb;
}
void retro_set_input_poll(retro_input_poll_t cb)
{
input_poll_cb = cb;
}
void retro_set_input_state(retro_input_state_t cb)
{
input_state_cb = cb;
}
void retro_set_video_refresh(retro_video_refresh_t cb)
{
video_cb = cb;
}
static uint32_t find_memory_type_from_requirements(
uint32_t device_requirements, uint32_t host_requirements)
{
const VkPhysicalDeviceMemoryProperties *props = &vk.memory_properties;
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++)
{
if (device_requirements & (1u << i))
{
if ((props->memoryTypes[i].propertyFlags & host_requirements) == host_requirements)
{
return i;
}
}
}
return 0;
}
static void update_ubo(void)
{
static unsigned frame;
float c = cosf(frame * 0.01f);
float s = sinf(frame * 0.01f);
frame++;
float tmp[16] = {0.0f};
tmp[ 0] = c;
tmp[ 1] = s;
tmp[ 4] = -s;
tmp[ 5] = c;
tmp[10] = 1.0f;
tmp[15] = 1.0f;
float *mvp = NULL;
vkMapMemory(vulkan->device, vk.ubo[vk.index].memory,
0, 16 * sizeof(float), 0, (void**)&mvp);
memcpy(mvp, tmp, sizeof(tmp));
vkUnmapMemory(vulkan->device, vk.ubo[vk.index].memory);
}
static void vulkan_test_render(void)
{
update_ubo();
VkCommandBuffer cmd = vk.cmd[vk.index];
vkResetCommandBuffer(cmd, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(cmd, &begin_info);
VkImageMemoryBarrier prepare_rendering = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
prepare_rendering.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
prepare_rendering.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
prepare_rendering.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
prepare_rendering.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
prepare_rendering.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
prepare_rendering.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
prepare_rendering.image = vk.images[vk.index].create_info.image;
prepare_rendering.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
prepare_rendering.subresourceRange.levelCount = 1;
prepare_rendering.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(cmd,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
false,
0, NULL,
0, NULL,
1, &prepare_rendering);
VkClearValue clear_value;
clear_value.color.float32[0] = 0.8f;
clear_value.color.float32[1] = 0.6f;
clear_value.color.float32[2] = 0.2f;
clear_value.color.float32[3] = 1.0f;
VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
rp_begin.renderPass = vk.render_pass;
rp_begin.framebuffer = vk.framebuffers[vk.index];
rp_begin.renderArea.extent.width = BASE_WIDTH;
rp_begin.renderArea.extent.height = BASE_HEIGHT;
rp_begin.clearValueCount = 1;
rp_begin.pClearValues = &clear_value;
vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, vk.pipeline);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
vk.pipeline_layout, 0,
1, &vk.desc_set[vk.index], 0, NULL);
VkViewport vp = { 0 };
vp.x = 0.0f;
vp.y = 0.0f;
vp.width = BASE_WIDTH;
vp.height = BASE_HEIGHT;
vp.minDepth = 0.0f;
vp.maxDepth = 1.0f;
vkCmdSetViewport(cmd, 0, 1, &vp);
VkRect2D scissor;
memset(&scissor, 0, sizeof(scissor));
scissor.extent.width = BASE_WIDTH;
scissor.extent.height = BASE_HEIGHT;
vkCmdSetScissor(cmd, 0, 1, &scissor);
vkCmdSetLineWidth(cmd, 1.0f);
vkCmdSetDepthBias(cmd, 0.0f, 0.0f, 0.0f);
vkCmdSetDepthBounds(cmd, 0.0f, 1.0f);
vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FACE_FRONT_BIT | VK_STENCIL_FACE_BACK_BIT, 0xff);
vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FACE_FRONT_BIT | VK_STENCIL_FACE_BACK_BIT, 0xff);
vkCmdSetStencilReference(cmd, VK_STENCIL_FACE_FRONT_BIT | VK_STENCIL_FACE_BACK_BIT, 0);
static const float blend_const[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
vkCmdSetBlendConstants(cmd, blend_const);
VkDeviceSize offset = 0;
vkCmdBindVertexBuffers(cmd, 0, 1, &vk.vbo.buffer, &offset);
vkCmdDraw(cmd, 3, 1, 0, 0);
vkCmdEndRenderPass(cmd);
VkImageMemoryBarrier prepare_presentation = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
prepare_presentation.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
prepare_presentation.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
prepare_presentation.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
prepare_presentation.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
prepare_presentation.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
prepare_presentation.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
prepare_presentation.image = vk.images[vk.index].create_info.image;
prepare_presentation.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
prepare_presentation.subresourceRange.levelCount = 1;
prepare_presentation.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
false,
0, NULL,
0, NULL,
1, &prepare_presentation);
vkEndCommandBuffer(cmd);
}
static struct buffer create_buffer(const void *initial, size_t size, VkBufferUsageFlags usage)
{
struct buffer buffer;
VkDevice device = vulkan->device;
VkBufferCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
info.usage = usage;
info.size = size;
vkCreateBuffer(device, &info, NULL, &buffer.buffer);
VkMemoryRequirements mem_reqs;
vkGetBufferMemoryRequirements(device, buffer.buffer, &mem_reqs);
VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
alloc.allocationSize = mem_reqs.size;
alloc.memoryTypeIndex = find_memory_type_from_requirements(mem_reqs.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
vkAllocateMemory(device, &alloc, NULL, &buffer.memory);
vkBindBufferMemory(device, buffer.buffer, buffer.memory, 0);
if (initial)
{
void *ptr;
vkMapMemory(device, buffer.memory, 0, size, 0, &ptr);
memcpy(ptr, initial, size);
vkUnmapMemory(device, buffer.memory);
}
return buffer;
}
static void init_vertex_buffer(void)
{
static const float data[] = {
-0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // vec4 position, vec4 color
-0.5f, +0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
};
vk.vbo = create_buffer(data, sizeof(data), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
}
static void init_uniform_buffer(void)
{
for (unsigned i = 0; i < vk.num_swapchain_images; i++)
{
vk.ubo[i] = create_buffer(NULL, 16 * sizeof(float),
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
}
}
static VkShaderModule create_shader_module(const uint8_t *data, size_t size)
{
VkShaderModuleCreateInfo module_info = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
VkShaderModule module;
module_info.codeSize = size;
module_info.pCode = (const uint32_t*)data;
vkCreateShaderModule(vulkan->device, &module_info, NULL, &module);
return module;
}
static void init_descriptor(void)
{
VkDevice device = vulkan->device;
VkDescriptorSetLayoutBinding binding = {0};
binding.binding = 0;
binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
binding.descriptorCount = 1;
binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
binding.pImmutableSamplers = NULL;
static const VkDescriptorPoolSize pool_sizes[1] = {
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 },
};
VkDescriptorSetLayoutCreateInfo set_layout_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
set_layout_info.bindingCount = 1;
set_layout_info.pBindings = &binding;
vkCreateDescriptorSetLayout(device, &set_layout_info, NULL, &vk.set_layout);
VkPipelineLayoutCreateInfo layout_info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &vk.set_layout;
vkCreatePipelineLayout(device, &layout_info, NULL, &vk.pipeline_layout);
VkDescriptorPoolCreateInfo pool_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
pool_info.maxSets = vk.num_swapchain_images;
pool_info.poolSizeCount = 1;
pool_info.pPoolSizes = pool_sizes;
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
vkCreateDescriptorPool(device, &pool_info, NULL, &vk.desc_pool);
VkDescriptorSetAllocateInfo alloc_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
alloc_info.descriptorPool = vk.desc_pool;
alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &vk.set_layout;
for (unsigned i = 0; i < vk.num_swapchain_images; i++)
{
vkAllocateDescriptorSets(device, &alloc_info, &vk.desc_set[i]);
VkWriteDescriptorSet write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
VkDescriptorBufferInfo buffer_info;
write.dstSet = vk.desc_set[i];
write.dstBinding = 0;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
write.pBufferInfo = &buffer_info;
buffer_info.buffer = vk.ubo[i].buffer;
buffer_info.offset = 0;
buffer_info.range = 16 * sizeof(float);
vkUpdateDescriptorSets(device, 1, &write, 0, NULL);
}
}
static void init_pipeline(void)
{
VkDevice device = vulkan->device;
VkPipelineInputAssemblyStateCreateInfo input_assembly = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkVertexInputAttributeDescription attributes[2] = {{ 0 }};
attributes[0].location = 0;
attributes[0].binding = 0;
attributes[0].format = VK_FORMAT_R32G32B32A32_SFLOAT;
attributes[0].offset = 0;
attributes[1].location = 1;
attributes[1].binding = 0;
attributes[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
attributes[1].offset = 4 * sizeof(float);
VkVertexInputBindingDescription binding = { 0 };
binding.binding = 0;
binding.stride = sizeof(float) * 8;
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkPipelineVertexInputStateCreateInfo vertex_input = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
vertex_input.vertexBindingDescriptionCount = 1;
vertex_input.pVertexBindingDescriptions = &binding;
vertex_input.vertexAttributeDescriptionCount = 2;
vertex_input.pVertexAttributeDescriptions = attributes;
VkPipelineRasterizationStateCreateInfo raster = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
raster.polygonMode = VK_POLYGON_MODE_FILL;
raster.cullMode = VK_CULL_MODE_BACK_BIT;
raster.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
raster.depthClampEnable = false;
raster.rasterizerDiscardEnable = false;
raster.depthBiasEnable = false;
VkPipelineColorBlendAttachmentState blend_attachment = { 0 };
blend_attachment.blendEnable = false;
blend_attachment.colorWriteMask = 0xf;
VkPipelineColorBlendStateCreateInfo blend = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
blend.attachmentCount = 1;
blend.pAttachments = &blend_attachment;
VkPipelineViewportStateCreateInfo viewport = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
viewport.viewportCount = 1;
viewport.scissorCount = 1;
VkPipelineDepthStencilStateCreateInfo depth_stencil = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
depth_stencil.depthTestEnable = false;
depth_stencil.depthWriteEnable = false;
depth_stencil.depthBoundsTestEnable = false;
depth_stencil.stencilTestEnable = false;
VkPipelineMultisampleStateCreateInfo multisample = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
static const VkDynamicState dynamics[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_LINE_WIDTH,
VK_DYNAMIC_STATE_DEPTH_BIAS,
VK_DYNAMIC_STATE_BLEND_CONSTANTS,
VK_DYNAMIC_STATE_DEPTH_BOUNDS,
VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK,
VK_DYNAMIC_STATE_STENCIL_REFERENCE,
};
VkPipelineDynamicStateCreateInfo dynamic = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
dynamic.pDynamicStates = dynamics;
dynamic.dynamicStateCount = sizeof(dynamics) / sizeof(dynamics[0]);
VkPipelineShaderStageCreateInfo shader_stages[2] = {
{ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO },
{ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO },
};
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shader_stages[0].module = create_shader_module(triangle_vert_spv, triangle_vert_spv_len);
shader_stages[0].pName = "main";
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader_stages[1].module = create_shader_module(triangle_frag_spv, triangle_frag_spv_len);
shader_stages[1].pName = "main";
VkGraphicsPipelineCreateInfo pipe = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
pipe.stageCount = 2;
pipe.pStages = shader_stages;
pipe.pVertexInputState = &vertex_input;
pipe.pInputAssemblyState = &input_assembly;
pipe.pRasterizationState = &raster;
pipe.pColorBlendState = &blend;
pipe.pMultisampleState = &multisample;
pipe.pViewportState = &viewport;
pipe.pDepthStencilState = &depth_stencil;
pipe.pDynamicState = &dynamic;
pipe.renderPass = vk.render_pass;
pipe.layout = vk.pipeline_layout;
vkCreateGraphicsPipelines(vulkan->device, vk.pipeline_cache, 1, &pipe, NULL, &vk.pipeline);
vkDestroyShaderModule(device, shader_stages[0].module, NULL);
vkDestroyShaderModule(device, shader_stages[1].module, NULL);
}
static void init_render_pass(VkFormat format)
{
VkAttachmentDescription attachment = { 0 };
attachment.format = format;
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference color_ref = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
VkSubpassDescription subpass = { 0 };
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_ref;
VkRenderPassCreateInfo rp_info = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
rp_info.attachmentCount = 1;
rp_info.pAttachments = &attachment;
rp_info.subpassCount = 1;
rp_info.pSubpasses = &subpass;
vkCreateRenderPass(vulkan->device, &rp_info, NULL, &vk.render_pass);
}
static void init_swapchain(void)
{
VkDevice device = vulkan->device;
for (unsigned i = 0; i < vk.num_swapchain_images; i++)
{
VkImageCreateInfo image = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
image.imageType = VK_IMAGE_TYPE_2D;
image.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
image.format = VK_FORMAT_R8G8B8A8_UNORM;
image.extent.width = BASE_WIDTH;
image.extent.height = BASE_HEIGHT;
image.extent.depth = 1;
image.samples = VK_SAMPLE_COUNT_1_BIT;
image.tiling = VK_IMAGE_TILING_LINEAR; /* Workaround. */
image.usage =
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
image.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image.mipLevels = 1;
image.arrayLayers = 1;
vkCreateImage(device, &image, NULL, &vk.images[i].create_info.image);
VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
VkMemoryRequirements mem_reqs;
vkGetImageMemoryRequirements(device, vk.images[i].create_info.image, &mem_reqs);
alloc.allocationSize = mem_reqs.size;
alloc.memoryTypeIndex = find_memory_type_from_requirements(
mem_reqs.memoryTypeBits, 0);
vkAllocateMemory(device, &alloc, NULL, &vk.image_memory[i]);
vkBindImageMemory(device, vk.images[i].create_info.image, vk.image_memory[i], 0);
vk.images[i].create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
vk.images[i].create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
vk.images[i].create_info.subresourceRange.baseMipLevel = 0;
vk.images[i].create_info.subresourceRange.baseArrayLayer = 0;
vk.images[i].create_info.subresourceRange.levelCount = 1;
vk.images[i].create_info.subresourceRange.layerCount = 1;
vk.images[i].create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
vk.images[i].create_info.components.r = VK_COMPONENT_SWIZZLE_R;
vk.images[i].create_info.components.g = VK_COMPONENT_SWIZZLE_G;
vk.images[i].create_info.components.b = VK_COMPONENT_SWIZZLE_B;
vk.images[i].create_info.components.a = VK_COMPONENT_SWIZZLE_A;
vkCreateImageView(device, &vk.images[i].create_info,
NULL, &vk.images[i].image_view);
vk.images[i].image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
fb_info.renderPass = vk.render_pass;
fb_info.attachmentCount = 1;
fb_info.pAttachments = &vk.images[i].image_view;
fb_info.width = BASE_WIDTH;
fb_info.height = BASE_HEIGHT;
fb_info.layers = 1;
vkCreateFramebuffer(device, &fb_info, NULL, &vk.framebuffers[i]);
}
}
static void init_command(void)
{
VkCommandPoolCreateInfo pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
VkCommandBufferAllocateInfo info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
pool_info.queueFamilyIndex = vulkan->queue_index;
pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
vkCreateCommandPool(vulkan->device, &pool_info, NULL, &vk.cmd_pool);
info.commandPool = vk.cmd_pool;
info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
info.commandBufferCount = vk.num_swapchain_images;
vkAllocateCommandBuffers(vulkan->device, &info, vk.cmd);
}
static void vulkan_test_init(void)
{
vkGetPhysicalDeviceProperties(vulkan->gpu, &vk.gpu_properties);
vkGetPhysicalDeviceMemoryProperties(vulkan->gpu, &vk.memory_properties);
unsigned num_images = 0;
uint32_t mask = vulkan->get_sync_index_mask(vulkan->handle);
for (unsigned i = 0; i < 32; i++)
if (mask & (1u << i))
num_images = i + 1;
vk.num_swapchain_images = num_images;
vk.swapchain_mask = mask;
init_uniform_buffer();
init_vertex_buffer();
init_command();
init_descriptor();
VkPipelineCacheCreateInfo pipeline_cache_info = { VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
vkCreatePipelineCache(vulkan->device, &pipeline_cache_info,
NULL, &vk.pipeline_cache);
init_render_pass(VK_FORMAT_R8G8B8A8_UNORM);
init_pipeline();
init_swapchain();
}
static void vulkan_test_deinit(void)
{
VkDevice device = vulkan->device;
vkDeviceWaitIdle(device);
for (unsigned i = 0; i < vk.num_swapchain_images; i++)
{
vkDestroyFramebuffer(device, vk.framebuffers[i], NULL);
vkDestroyImageView(device, vk.images[i].image_view, NULL);
vkFreeMemory(device, vk.image_memory[i], NULL);
vkDestroyImage(device, vk.images[i].create_info.image, NULL);
vkFreeMemory(device, vk.ubo[i].memory, NULL);
vkDestroyBuffer(device, vk.ubo[i].buffer, NULL);
}
vkFreeDescriptorSets(device, vk.desc_pool, vk.num_swapchain_images, vk.desc_set);
vkDestroyDescriptorPool(device, vk.desc_pool, NULL);
vkDestroyRenderPass(device, vk.render_pass, NULL);
vkDestroyPipeline(device, vk.pipeline, NULL);
vkDestroyPipelineLayout(device, vk.pipeline_layout, NULL);
vkFreeMemory(device, vk.vbo.memory, NULL);
vkDestroyBuffer(device, vk.vbo.buffer, NULL);
vkDestroyPipelineCache(device, vk.pipeline_cache, NULL);
vkFreeCommandBuffers(device, vk.cmd_pool, vk.num_swapchain_images, vk.cmd);
vkDestroyCommandPool(device, vk.cmd_pool, NULL);
memset(&vk, 0, sizeof(vk));
}
void retro_run(void)
{
/* Very lazy way to do this. */
if (vulkan->get_sync_index_mask(vulkan->handle) != vk.swapchain_mask)
{
vulkan_test_deinit();
vulkan_test_init();
}
vulkan->wait_sync_index(vulkan->handle);
input_poll_cb();
vk.index = vulkan->get_sync_index(vulkan->handle);
vulkan_test_render();
vulkan->set_image(vulkan->handle, &vk.images[vk.index], 0, NULL);
vulkan->set_command_buffers(vulkan->handle, 1, &vk.cmd[vk.index]);
video_cb(RETRO_HW_FRAME_BUFFER_VALID, BASE_WIDTH, BASE_HEIGHT, 0);
}
static void context_reset(void)
{
fprintf(stderr, "Context reset!\n");
if (!environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void**)&vulkan) || !vulkan)
{
fprintf(stderr, "Failed to get HW rendering interface!\n");
return;
}
vulkan_test_init();
}
static void context_destroy(void)
{
fprintf(stderr, "Context destroy!\n");
vulkan_test_deinit();
vulkan = NULL;
memset(&vk, 0, sizeof(vk));
}
static bool retro_init_hw_context(void)
{
hw_render.context_type = RETRO_HW_CONTEXT_VULKAN;
hw_render.version_major = VK_API_VERSION;
hw_render.version_minor = 0;
hw_render.context_reset = context_reset;
hw_render.context_destroy = context_destroy;
hw_render.cache_context = true;
if (!environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render))
return false;
return true;
}
bool retro_load_game(const struct retro_game_info *info)
{
if (!retro_init_hw_context())
{
fprintf(stderr, "HW Context could not be initialized, exiting...\n");
return false;
}
fprintf(stderr, "Loaded game!\n");
(void)info;
return true;
}
void retro_unload_game(void)
{}
unsigned retro_get_region(void)
{
return RETRO_REGION_NTSC;
}
bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num)
{
(void)type;
(void)info;
(void)num;
return false;
}
size_t retro_serialize_size(void)
{
return 0;
}
bool retro_serialize(void *data, size_t size)
{
(void)data;
(void)size;
return false;
}
bool retro_unserialize(const void *data, size_t size)
{
(void)data;
(void)size;
return false;
}
void *retro_get_memory_data(unsigned id)
{
(void)id;
return NULL;
}
size_t retro_get_memory_size(unsigned id)
{
(void)id;
return 0;
}
void retro_reset(void)
{}
void retro_cheat_reset(void)
{}
void retro_cheat_set(unsigned index, bool enabled, const char *code)
{
(void)index;
(void)enabled;
(void)code;
}

View File

@ -0,0 +1,5 @@
{
global: retro_*;
local: *;
};

View File

@ -0,0 +1,23 @@
VERT_SHADERS := $(wildcard *.vert)
FRAG_SHADERS := $(wildcard *.frag)
SPIRV := $(VERT_SHADERS:.vert=.vert.spv) $(FRAG_SHADERS:.frag=.frag.spv)
INCLUDES := $(SPIRV:.spv=.inc)
GLSLANG := glslangValidator
all: $(INCLUDES)
%.frag.spv: %.frag
$(GLSLANG) -V -o $@ $<
%.vert.spv: %.vert
$(GLSLANG) -V -o $@ $<
%.inc: %.spv
xxd -i $< $@
clean:
rm -f $(INCLUDES)
rm -f $(SPIRV)
.PHONY: clean

View File

@ -0,0 +1,9 @@
#version 310 es
precision mediump float;
layout(location = 0) in vec4 vColor;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = vColor;
}

View File

@ -0,0 +1,37 @@
unsigned char triangle_frag_spv[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
0x01, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x46, 0x72, 0x61, 0x67,
0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x76, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00,
0x47, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00,
0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00,
0x05, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
0x09, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00
};
unsigned int triangle_frag_spv_len = 400;

View File

@ -0,0 +1,15 @@
#version 310 es
layout(location = 0) in vec4 Position;
layout(location = 1) in vec4 Color;
layout(location = 0) out vec4 vColor;
layout(std140, set = 0, binding = 0) uniform UBO
{
mat4 MVP;
};
void main()
{
gl_Position = MVP * Position;
vColor = Color;
}

View File

@ -0,0 +1,94 @@
unsigned char triangle_vert_spv[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x15, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
0x01, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50,
0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00,
0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x55, 0x42, 0x4f, 0x00, 0x06, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4d, 0x56, 0x50, 0x00, 0x05, 0x00, 0x03, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
0x15, 0x00, 0x00, 0x00, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00,
0x76, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x1b, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x56,
0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x44, 0x00, 0x05, 0x00, 0x06, 0x00,
0x1f, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x49, 0x6e, 0x73, 0x74, 0x61,
0x6e, 0x63, 0x65, 0x49, 0x44, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x48, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x1a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x1f, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00,
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00,
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x15, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00,
0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00,
0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00,
0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x1d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00,
0x1f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00,
0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00,
0x0d, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x15, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00,
0x17, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00,
0x3e, 0x00, 0x03, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00
};
unsigned int triangle_vert_spv_len = 1088;

View File

@ -120,6 +120,8 @@ else
CFLAGS += -std=gnu99 CFLAGS += -std=gnu99
endif endif
CFLAGS += -I../../libretro-common/include
all: $(TARGET) all: $(TARGET)
$(TARGET): $(OBJECTS) $(TARGET): $(OBJECTS)

View File

@ -9,7 +9,7 @@
#include "../../libretro.h" #include "../../libretro.h"
static uint16_t *frame_buf; static uint32_t *frame_buf;
static struct retro_log_callback logging; static struct retro_log_callback logging;
static retro_log_printf_t log_cb; static retro_log_printf_t log_cb;
static bool use_audio_cb; static bool use_audio_cb;
@ -27,7 +27,7 @@ static void fallback_log(enum retro_log_level level, const char *fmt, ...)
void retro_init(void) void retro_init(void)
{ {
frame_buf = calloc(320 * 240, sizeof(uint16_t)); frame_buf = calloc(320 * 240, sizeof(uint32_t));
} }
void retro_deinit(void) void retro_deinit(void)
@ -278,11 +278,29 @@ static void update_input(void)
static void render_checkered(void) static void render_checkered(void)
{ {
uint16_t color_r = 31 << 11; /* Try rendering straight into VRAM if we can. */
uint16_t color_g = 63 << 5; uint32_t *buf = NULL;
unsigned stride = 0;
struct retro_framebuffer fb = {0};
fb.width = 320;
fb.height = 240;
fb.access_flags = RETRO_MEMORY_ACCESS_WRITE;
if (environ_cb(RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER, &fb) && fb.format == RETRO_PIXEL_FORMAT_XRGB8888)
{
buf = fb.data;
stride = fb.pitch >> 2;
}
else
{
buf = frame_buf;
stride = 320;
}
uint16_t *line = frame_buf; uint32_t color_r = 0xff << 16;
for (unsigned y = 0; y < 240; y++, line += 320) uint32_t color_g = 0xff << 8;
uint32_t *line = buf;
for (unsigned y = 0; y < 240; y++, line += stride)
{ {
unsigned index_y = ((y - y_coord) >> 4) & 1; unsigned index_y = ((y - y_coord) >> 4) & 1;
for (unsigned x = 0; x < 320; x++) for (unsigned x = 0; x < 320; x++)
@ -294,9 +312,9 @@ static void render_checkered(void)
for (unsigned y = mouse_rel_y - 5; y <= mouse_rel_y + 5; y++) for (unsigned y = mouse_rel_y - 5; y <= mouse_rel_y + 5; y++)
for (unsigned x = mouse_rel_x - 5; x <= mouse_rel_x + 5; x++) for (unsigned x = mouse_rel_x - 5; x <= mouse_rel_x + 5; x++)
frame_buf[y * 320 + x] = 0x1f; buf[y * stride + x] = 0xff;
video_cb(frame_buf, 320, 240, 320 << 1); video_cb(buf, 320, 240, stride << 2);
} }
static void check_variables(void) static void check_variables(void)
@ -381,10 +399,10 @@ bool retro_load_game(const struct retro_game_info *info)
environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc); environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565; enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
{ {
log_cb(RETRO_LOG_INFO, "RGB565 is not supported.\n"); log_cb(RETRO_LOG_INFO, "XRGB8888 is not supported.\n");
return false; return false;
} }

1
deps/glslang/glslang vendored Submodule

@ -0,0 +1 @@
Subproject commit 9d565d9ef802c6984122d182bc1fc477dd5e07ba

384
deps/glslang/glslang.cpp vendored Normal file
View File

@ -0,0 +1,384 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2016 - Hans-Kristian Arntzen
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "glslang.hpp"
#include "ShaderLang.h"
#include "GlslangToSpv.h"
#include <vector>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include "../../general.h"
using namespace glslang;
using namespace std;
struct SlangProcess
{
public:
SlangProcess();
TBuiltInResource& GetResources() { return Resources; }
~SlangProcess() { FinalizeProcess(); }
private:
TBuiltInResource Resources;
};
SlangProcess::SlangProcess()
{
InitializeProcess();
char DefaultConfig[] =
"MaxLights 32\n"
"MaxClipPlanes 6\n"
"MaxTextureUnits 32\n"
"MaxTextureCoords 32\n"
"MaxVertexAttribs 64\n"
"MaxVertexUniformComponents 4096\n"
"MaxVaryingFloats 64\n"
"MaxVertexTextureImageUnits 32\n"
"MaxCombinedTextureImageUnits 80\n"
"MaxTextureImageUnits 32\n"
"MaxFragmentUniformComponents 4096\n"
"MaxDrawBuffers 32\n"
"MaxVertexUniformVectors 128\n"
"MaxVaryingVectors 8\n"
"MaxFragmentUniformVectors 16\n"
"MaxVertexOutputVectors 16\n"
"MaxFragmentInputVectors 15\n"
"MinProgramTexelOffset -8\n"
"MaxProgramTexelOffset 7\n"
"MaxClipDistances 8\n"
"MaxComputeWorkGroupCountX 65535\n"
"MaxComputeWorkGroupCountY 65535\n"
"MaxComputeWorkGroupCountZ 65535\n"
"MaxComputeWorkGroupSizeX 1024\n"
"MaxComputeWorkGroupSizeY 1024\n"
"MaxComputeWorkGroupSizeZ 64\n"
"MaxComputeUniformComponents 1024\n"
"MaxComputeTextureImageUnits 16\n"
"MaxComputeImageUniforms 8\n"
"MaxComputeAtomicCounters 8\n"
"MaxComputeAtomicCounterBuffers 1\n"
"MaxVaryingComponents 60\n"
"MaxVertexOutputComponents 64\n"
"MaxGeometryInputComponents 64\n"
"MaxGeometryOutputComponents 128\n"
"MaxFragmentInputComponents 128\n"
"MaxImageUnits 8\n"
"MaxCombinedImageUnitsAndFragmentOutputs 8\n"
"MaxCombinedShaderOutputResources 8\n"
"MaxImageSamples 0\n"
"MaxVertexImageUniforms 0\n"
"MaxTessControlImageUniforms 0\n"
"MaxTessEvaluationImageUniforms 0\n"
"MaxGeometryImageUniforms 0\n"
"MaxFragmentImageUniforms 8\n"
"MaxCombinedImageUniforms 8\n"
"MaxGeometryTextureImageUnits 16\n"
"MaxGeometryOutputVertices 256\n"
"MaxGeometryTotalOutputComponents 1024\n"
"MaxGeometryUniformComponents 1024\n"
"MaxGeometryVaryingComponents 64\n"
"MaxTessControlInputComponents 128\n"
"MaxTessControlOutputComponents 128\n"
"MaxTessControlTextureImageUnits 16\n"
"MaxTessControlUniformComponents 1024\n"
"MaxTessControlTotalOutputComponents 4096\n"
"MaxTessEvaluationInputComponents 128\n"
"MaxTessEvaluationOutputComponents 128\n"
"MaxTessEvaluationTextureImageUnits 16\n"
"MaxTessEvaluationUniformComponents 1024\n"
"MaxTessPatchComponents 120\n"
"MaxPatchVertices 32\n"
"MaxTessGenLevel 64\n"
"MaxViewports 16\n"
"MaxVertexAtomicCounters 0\n"
"MaxTessControlAtomicCounters 0\n"
"MaxTessEvaluationAtomicCounters 0\n"
"MaxGeometryAtomicCounters 0\n"
"MaxFragmentAtomicCounters 8\n"
"MaxCombinedAtomicCounters 8\n"
"MaxAtomicCounterBindings 1\n"
"MaxVertexAtomicCounterBuffers 0\n"
"MaxTessControlAtomicCounterBuffers 0\n"
"MaxTessEvaluationAtomicCounterBuffers 0\n"
"MaxGeometryAtomicCounterBuffers 0\n"
"MaxFragmentAtomicCounterBuffers 1\n"
"MaxCombinedAtomicCounterBuffers 1\n"
"MaxAtomicCounterBufferSize 16384\n"
"MaxTransformFeedbackBuffers 4\n"
"MaxTransformFeedbackInterleavedComponents 64\n"
"MaxCullDistances 8\n"
"MaxCombinedClipAndCullDistances 8\n"
"MaxSamples 4\n"
"nonInductiveForLoops 1\n"
"whileLoops 1\n"
"doWhileLoops 1\n"
"generalUniformIndexing 1\n"
"generalAttributeMatrixVectorIndexing 1\n"
"generalVaryingIndexing 1\n"
"generalSamplerIndexing 1\n"
"generalVariableIndexing 1\n"
"generalConstantMatrixVectorIndexing 1\n";
const char *delims = " \t\n\r";
const char *token = strtok(DefaultConfig, delims);
while (token)
{
const char *value_str = strtok(0, delims);
int value = strtoul(value_str, nullptr, 0);
if (strcmp(token, "MaxLights") == 0)
Resources.maxLights = value;
else if (strcmp(token, "MaxClipPlanes") == 0)
Resources.maxClipPlanes = value;
else if (strcmp(token, "MaxTextureUnits") == 0)
Resources.maxTextureUnits = value;
else if (strcmp(token, "MaxTextureCoords") == 0)
Resources.maxTextureCoords = value;
else if (strcmp(token, "MaxVertexAttribs") == 0)
Resources.maxVertexAttribs = value;
else if (strcmp(token, "MaxVertexUniformComponents") == 0)
Resources.maxVertexUniformComponents = value;
else if (strcmp(token, "MaxVaryingFloats") == 0)
Resources.maxVaryingFloats = value;
else if (strcmp(token, "MaxVertexTextureImageUnits") == 0)
Resources.maxVertexTextureImageUnits = value;
else if (strcmp(token, "MaxCombinedTextureImageUnits") == 0)
Resources.maxCombinedTextureImageUnits = value;
else if (strcmp(token, "MaxTextureImageUnits") == 0)
Resources.maxTextureImageUnits = value;
else if (strcmp(token, "MaxFragmentUniformComponents") == 0)
Resources.maxFragmentUniformComponents = value;
else if (strcmp(token, "MaxDrawBuffers") == 0)
Resources.maxDrawBuffers = value;
else if (strcmp(token, "MaxVertexUniformVectors") == 0)
Resources.maxVertexUniformVectors = value;
else if (strcmp(token, "MaxVaryingVectors") == 0)
Resources.maxVaryingVectors = value;
else if (strcmp(token, "MaxFragmentUniformVectors") == 0)
Resources.maxFragmentUniformVectors = value;
else if (strcmp(token, "MaxVertexOutputVectors") == 0)
Resources.maxVertexOutputVectors = value;
else if (strcmp(token, "MaxFragmentInputVectors") == 0)
Resources.maxFragmentInputVectors = value;
else if (strcmp(token, "MinProgramTexelOffset") == 0)
Resources.minProgramTexelOffset = value;
else if (strcmp(token, "MaxProgramTexelOffset") == 0)
Resources.maxProgramTexelOffset = value;
else if (strcmp(token, "MaxClipDistances") == 0)
Resources.maxClipDistances = value;
else if (strcmp(token, "MaxComputeWorkGroupCountX") == 0)
Resources.maxComputeWorkGroupCountX = value;
else if (strcmp(token, "MaxComputeWorkGroupCountY") == 0)
Resources.maxComputeWorkGroupCountY = value;
else if (strcmp(token, "MaxComputeWorkGroupCountZ") == 0)
Resources.maxComputeWorkGroupCountZ = value;
else if (strcmp(token, "MaxComputeWorkGroupSizeX") == 0)
Resources.maxComputeWorkGroupSizeX = value;
else if (strcmp(token, "MaxComputeWorkGroupSizeY") == 0)
Resources.maxComputeWorkGroupSizeY = value;
else if (strcmp(token, "MaxComputeWorkGroupSizeZ") == 0)
Resources.maxComputeWorkGroupSizeZ = value;
else if (strcmp(token, "MaxComputeUniformComponents") == 0)
Resources.maxComputeUniformComponents = value;
else if (strcmp(token, "MaxComputeTextureImageUnits") == 0)
Resources.maxComputeTextureImageUnits = value;
else if (strcmp(token, "MaxComputeImageUniforms") == 0)
Resources.maxComputeImageUniforms = value;
else if (strcmp(token, "MaxComputeAtomicCounters") == 0)
Resources.maxComputeAtomicCounters = value;
else if (strcmp(token, "MaxComputeAtomicCounterBuffers") == 0)
Resources.maxComputeAtomicCounterBuffers = value;
else if (strcmp(token, "MaxVaryingComponents") == 0)
Resources.maxVaryingComponents = value;
else if (strcmp(token, "MaxVertexOutputComponents") == 0)
Resources.maxVertexOutputComponents = value;
else if (strcmp(token, "MaxGeometryInputComponents") == 0)
Resources.maxGeometryInputComponents = value;
else if (strcmp(token, "MaxGeometryOutputComponents") == 0)
Resources.maxGeometryOutputComponents = value;
else if (strcmp(token, "MaxFragmentInputComponents") == 0)
Resources.maxFragmentInputComponents = value;
else if (strcmp(token, "MaxImageUnits") == 0)
Resources.maxImageUnits = value;
else if (strcmp(token, "MaxCombinedImageUnitsAndFragmentOutputs") == 0)
Resources.maxCombinedImageUnitsAndFragmentOutputs = value;
else if (strcmp(token, "MaxCombinedShaderOutputResources") == 0)
Resources.maxCombinedShaderOutputResources = value;
else if (strcmp(token, "MaxImageSamples") == 0)
Resources.maxImageSamples = value;
else if (strcmp(token, "MaxVertexImageUniforms") == 0)
Resources.maxVertexImageUniforms = value;
else if (strcmp(token, "MaxTessControlImageUniforms") == 0)
Resources.maxTessControlImageUniforms = value;
else if (strcmp(token, "MaxTessEvaluationImageUniforms") == 0)
Resources.maxTessEvaluationImageUniforms = value;
else if (strcmp(token, "MaxGeometryImageUniforms") == 0)
Resources.maxGeometryImageUniforms = value;
else if (strcmp(token, "MaxFragmentImageUniforms") == 0)
Resources.maxFragmentImageUniforms = value;
else if (strcmp(token, "MaxCombinedImageUniforms") == 0)
Resources.maxCombinedImageUniforms = value;
else if (strcmp(token, "MaxGeometryTextureImageUnits") == 0)
Resources.maxGeometryTextureImageUnits = value;
else if (strcmp(token, "MaxGeometryOutputVertices") == 0)
Resources.maxGeometryOutputVertices = value;
else if (strcmp(token, "MaxGeometryTotalOutputComponents") == 0)
Resources.maxGeometryTotalOutputComponents = value;
else if (strcmp(token, "MaxGeometryUniformComponents") == 0)
Resources.maxGeometryUniformComponents = value;
else if (strcmp(token, "MaxGeometryVaryingComponents") == 0)
Resources.maxGeometryVaryingComponents = value;
else if (strcmp(token, "MaxTessControlInputComponents") == 0)
Resources.maxTessControlInputComponents = value;
else if (strcmp(token, "MaxTessControlOutputComponents") == 0)
Resources.maxTessControlOutputComponents = value;
else if (strcmp(token, "MaxTessControlTextureImageUnits") == 0)
Resources.maxTessControlTextureImageUnits = value;
else if (strcmp(token, "MaxTessControlUniformComponents") == 0)
Resources.maxTessControlUniformComponents = value;
else if (strcmp(token, "MaxTessControlTotalOutputComponents") == 0)
Resources.maxTessControlTotalOutputComponents = value;
else if (strcmp(token, "MaxTessEvaluationInputComponents") == 0)
Resources.maxTessEvaluationInputComponents = value;
else if (strcmp(token, "MaxTessEvaluationOutputComponents") == 0)
Resources.maxTessEvaluationOutputComponents = value;
else if (strcmp(token, "MaxTessEvaluationTextureImageUnits") == 0)
Resources.maxTessEvaluationTextureImageUnits = value;
else if (strcmp(token, "MaxTessEvaluationUniformComponents") == 0)
Resources.maxTessEvaluationUniformComponents = value;
else if (strcmp(token, "MaxTessPatchComponents") == 0)
Resources.maxTessPatchComponents = value;
else if (strcmp(token, "MaxPatchVertices") == 0)
Resources.maxPatchVertices = value;
else if (strcmp(token, "MaxTessGenLevel") == 0)
Resources.maxTessGenLevel = value;
else if (strcmp(token, "MaxViewports") == 0)
Resources.maxViewports = value;
else if (strcmp(token, "MaxVertexAtomicCounters") == 0)
Resources.maxVertexAtomicCounters = value;
else if (strcmp(token, "MaxTessControlAtomicCounters") == 0)
Resources.maxTessControlAtomicCounters = value;
else if (strcmp(token, "MaxTessEvaluationAtomicCounters") == 0)
Resources.maxTessEvaluationAtomicCounters = value;
else if (strcmp(token, "MaxGeometryAtomicCounters") == 0)
Resources.maxGeometryAtomicCounters = value;
else if (strcmp(token, "MaxFragmentAtomicCounters") == 0)
Resources.maxFragmentAtomicCounters = value;
else if (strcmp(token, "MaxCombinedAtomicCounters") == 0)
Resources.maxCombinedAtomicCounters = value;
else if (strcmp(token, "MaxAtomicCounterBindings") == 0)
Resources.maxAtomicCounterBindings = value;
else if (strcmp(token, "MaxVertexAtomicCounterBuffers") == 0)
Resources.maxVertexAtomicCounterBuffers = value;
else if (strcmp(token, "MaxTessControlAtomicCounterBuffers") == 0)
Resources.maxTessControlAtomicCounterBuffers = value;
else if (strcmp(token, "MaxTessEvaluationAtomicCounterBuffers") == 0)
Resources.maxTessEvaluationAtomicCounterBuffers = value;
else if (strcmp(token, "MaxGeometryAtomicCounterBuffers") == 0)
Resources.maxGeometryAtomicCounterBuffers = value;
else if (strcmp(token, "MaxFragmentAtomicCounterBuffers") == 0)
Resources.maxFragmentAtomicCounterBuffers = value;
else if (strcmp(token, "MaxCombinedAtomicCounterBuffers") == 0)
Resources.maxCombinedAtomicCounterBuffers = value;
else if (strcmp(token, "MaxAtomicCounterBufferSize") == 0)
Resources.maxAtomicCounterBufferSize = value;
else if (strcmp(token, "MaxTransformFeedbackBuffers") == 0)
Resources.maxTransformFeedbackBuffers = value;
else if (strcmp(token, "MaxTransformFeedbackInterleavedComponents") == 0)
Resources.maxTransformFeedbackInterleavedComponents = value;
else if (strcmp(token, "MaxCullDistances") == 0)
Resources.maxCullDistances = value;
else if (strcmp(token, "MaxCombinedClipAndCullDistances") == 0)
Resources.maxCombinedClipAndCullDistances = value;
else if (strcmp(token, "MaxSamples") == 0)
Resources.maxSamples = value;
else if (strcmp(token, "nonInductiveForLoops") == 0)
Resources.limits.nonInductiveForLoops = (value != 0);
else if (strcmp(token, "whileLoops") == 0)
Resources.limits.whileLoops = (value != 0);
else if (strcmp(token, "doWhileLoops") == 0)
Resources.limits.doWhileLoops = (value != 0);
else if (strcmp(token, "generalUniformIndexing") == 0)
Resources.limits.generalUniformIndexing = (value != 0);
else if (strcmp(token, "generalAttributeMatrixVectorIndexing") == 0)
Resources.limits.generalAttributeMatrixVectorIndexing = (value != 0);
else if (strcmp(token, "generalVaryingIndexing") == 0)
Resources.limits.generalVaryingIndexing = (value != 0);
else if (strcmp(token, "generalSamplerIndexing") == 0)
Resources.limits.generalSamplerIndexing = (value != 0);
else if (strcmp(token, "generalVariableIndexing") == 0)
Resources.limits.generalVariableIndexing = (value != 0);
else if (strcmp(token, "generalConstantMatrixVectorIndexing") == 0)
Resources.limits.generalConstantMatrixVectorIndexing = (value != 0);
token = strtok(0, delims);
}
}
bool glslang::compile_spirv(const string &source, Stage stage, std::vector<uint32_t> *spirv)
{
static SlangProcess process;
TProgram program;
EShLanguage language;
switch (stage)
{
case StageVertex: language = EShLangVertex; break;
case StageTessControl: language = EShLangTessControl; break;
case StageTessEvaluation: language = EShLangTessEvaluation; break;
case StageGeometry: language = EShLangGeometry; break;
case StageFragment: language = EShLangFragment; break;
case StageCompute: language = EShLangCompute; break;
default: return false;
}
TShader shader(language);
const char *src = source.c_str();
shader.setStrings(&src, 1);
string msg;
if (!shader.preprocess(&process.GetResources(), 100, ENoProfile, false, false, EShMsgDefault, &msg, TShader::ForbidInclude()))
{
fprintf(stderr, "%s\n", msg.c_str());
return {};
}
if (!shader.parse(&process.GetResources(), 100, false, EShMsgDefault))
{
RARCH_ERR("%s\n", shader.getInfoLog());
RARCH_ERR("%s\n", shader.getInfoDebugLog());
return false;
}
program.addShader(&shader);
if (!program.link(EShMsgDefault))
{
RARCH_ERR("%s\n", program.getInfoLog());
RARCH_ERR("%s\n", program.getInfoDebugLog());
return false;
}
GlslangToSpv(*program.getIntermediate(language), *spirv);
return true;
}

24
deps/glslang/glslang.hpp vendored Normal file
View File

@ -0,0 +1,24 @@
#ifndef GLSLANG_COMPILER_HPP
#define GLSLANG_COMPILER_HPP
#include <vector>
#include <string>
#include <stdint.h>
namespace glslang
{
enum Stage
{
StageVertex = 0,
StageTessControl,
StageTessEvaluation,
StageGeometry,
StageFragment,
StageCompute
};
bool compile_spirv(const std::string &source, Stage stage, std::vector<uint32_t> *spirv);
}
#endif

6805
deps/glslang/glslang_tab.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

316
deps/glslang/glslang_tab.cpp.h vendored Normal file
View File

@ -0,0 +1,316 @@
/* A Bison parser, made by GNU Bison 3.0.4. */
/* Bison interface for Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
#ifndef YY_YY_GLSLANG_TAB_CPP_H_INCLUDED
# define YY_YY_GLSLANG_TAB_CPP_H_INCLUDED
/* Debug traces. */
#ifndef YYDEBUG
# define YYDEBUG 1
#endif
#if YYDEBUG
extern int yydebug;
#endif
/* Token type. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
enum yytokentype
{
ATTRIBUTE = 258,
VARYING = 259,
CONST = 260,
BOOL = 261,
FLOAT = 262,
DOUBLE = 263,
INT = 264,
UINT = 265,
BREAK = 266,
CONTINUE = 267,
DO = 268,
ELSE = 269,
FOR = 270,
IF = 271,
DISCARD = 272,
RETURN = 273,
SWITCH = 274,
CASE = 275,
DEFAULT = 276,
SUBROUTINE = 277,
BVEC2 = 278,
BVEC3 = 279,
BVEC4 = 280,
IVEC2 = 281,
IVEC3 = 282,
IVEC4 = 283,
UVEC2 = 284,
UVEC3 = 285,
UVEC4 = 286,
VEC2 = 287,
VEC3 = 288,
VEC4 = 289,
MAT2 = 290,
MAT3 = 291,
MAT4 = 292,
CENTROID = 293,
IN = 294,
OUT = 295,
INOUT = 296,
UNIFORM = 297,
PATCH = 298,
SAMPLE = 299,
BUFFER = 300,
SHARED = 301,
COHERENT = 302,
VOLATILE = 303,
RESTRICT = 304,
READONLY = 305,
WRITEONLY = 306,
DVEC2 = 307,
DVEC3 = 308,
DVEC4 = 309,
DMAT2 = 310,
DMAT3 = 311,
DMAT4 = 312,
NOPERSPECTIVE = 313,
FLAT = 314,
SMOOTH = 315,
LAYOUT = 316,
MAT2X2 = 317,
MAT2X3 = 318,
MAT2X4 = 319,
MAT3X2 = 320,
MAT3X3 = 321,
MAT3X4 = 322,
MAT4X2 = 323,
MAT4X3 = 324,
MAT4X4 = 325,
DMAT2X2 = 326,
DMAT2X3 = 327,
DMAT2X4 = 328,
DMAT3X2 = 329,
DMAT3X3 = 330,
DMAT3X4 = 331,
DMAT4X2 = 332,
DMAT4X3 = 333,
DMAT4X4 = 334,
ATOMIC_UINT = 335,
SAMPLER1D = 336,
SAMPLER2D = 337,
SAMPLER3D = 338,
SAMPLERCUBE = 339,
SAMPLER1DSHADOW = 340,
SAMPLER2DSHADOW = 341,
SAMPLERCUBESHADOW = 342,
SAMPLER1DARRAY = 343,
SAMPLER2DARRAY = 344,
SAMPLER1DARRAYSHADOW = 345,
SAMPLER2DARRAYSHADOW = 346,
ISAMPLER1D = 347,
ISAMPLER2D = 348,
ISAMPLER3D = 349,
ISAMPLERCUBE = 350,
ISAMPLER1DARRAY = 351,
ISAMPLER2DARRAY = 352,
USAMPLER1D = 353,
USAMPLER2D = 354,
USAMPLER3D = 355,
USAMPLERCUBE = 356,
USAMPLER1DARRAY = 357,
USAMPLER2DARRAY = 358,
SAMPLER2DRECT = 359,
SAMPLER2DRECTSHADOW = 360,
ISAMPLER2DRECT = 361,
USAMPLER2DRECT = 362,
SAMPLERBUFFER = 363,
ISAMPLERBUFFER = 364,
USAMPLERBUFFER = 365,
SAMPLERCUBEARRAY = 366,
SAMPLERCUBEARRAYSHADOW = 367,
ISAMPLERCUBEARRAY = 368,
USAMPLERCUBEARRAY = 369,
SAMPLER2DMS = 370,
ISAMPLER2DMS = 371,
USAMPLER2DMS = 372,
SAMPLER2DMSARRAY = 373,
ISAMPLER2DMSARRAY = 374,
USAMPLER2DMSARRAY = 375,
SAMPLEREXTERNALOES = 376,
IMAGE1D = 377,
IIMAGE1D = 378,
UIMAGE1D = 379,
IMAGE2D = 380,
IIMAGE2D = 381,
UIMAGE2D = 382,
IMAGE3D = 383,
IIMAGE3D = 384,
UIMAGE3D = 385,
IMAGE2DRECT = 386,
IIMAGE2DRECT = 387,
UIMAGE2DRECT = 388,
IMAGECUBE = 389,
IIMAGECUBE = 390,
UIMAGECUBE = 391,
IMAGEBUFFER = 392,
IIMAGEBUFFER = 393,
UIMAGEBUFFER = 394,
IMAGE1DARRAY = 395,
IIMAGE1DARRAY = 396,
UIMAGE1DARRAY = 397,
IMAGE2DARRAY = 398,
IIMAGE2DARRAY = 399,
UIMAGE2DARRAY = 400,
IMAGECUBEARRAY = 401,
IIMAGECUBEARRAY = 402,
UIMAGECUBEARRAY = 403,
IMAGE2DMS = 404,
IIMAGE2DMS = 405,
UIMAGE2DMS = 406,
IMAGE2DMSARRAY = 407,
IIMAGE2DMSARRAY = 408,
UIMAGE2DMSARRAY = 409,
STRUCT = 410,
VOID = 411,
WHILE = 412,
IDENTIFIER = 413,
TYPE_NAME = 414,
FLOATCONSTANT = 415,
DOUBLECONSTANT = 416,
INTCONSTANT = 417,
UINTCONSTANT = 418,
BOOLCONSTANT = 419,
LEFT_OP = 420,
RIGHT_OP = 421,
INC_OP = 422,
DEC_OP = 423,
LE_OP = 424,
GE_OP = 425,
EQ_OP = 426,
NE_OP = 427,
AND_OP = 428,
OR_OP = 429,
XOR_OP = 430,
MUL_ASSIGN = 431,
DIV_ASSIGN = 432,
ADD_ASSIGN = 433,
MOD_ASSIGN = 434,
LEFT_ASSIGN = 435,
RIGHT_ASSIGN = 436,
AND_ASSIGN = 437,
XOR_ASSIGN = 438,
OR_ASSIGN = 439,
SUB_ASSIGN = 440,
LEFT_PAREN = 441,
RIGHT_PAREN = 442,
LEFT_BRACKET = 443,
RIGHT_BRACKET = 444,
LEFT_BRACE = 445,
RIGHT_BRACE = 446,
DOT = 447,
COMMA = 448,
COLON = 449,
EQUAL = 450,
SEMICOLON = 451,
BANG = 452,
DASH = 453,
TILDE = 454,
PLUS = 455,
STAR = 456,
SLASH = 457,
PERCENT = 458,
LEFT_ANGLE = 459,
RIGHT_ANGLE = 460,
VERTICAL_BAR = 461,
CARET = 462,
AMPERSAND = 463,
QUESTION = 464,
INVARIANT = 465,
PRECISE = 466,
HIGH_PRECISION = 467,
MEDIUM_PRECISION = 468,
LOW_PRECISION = 469,
PRECISION = 470,
PACKED = 471,
RESOURCE = 472,
SUPERP = 473
};
#endif
/* Value type. */
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
union YYSTYPE
{
#line 66 "glslang/glslang/MachineIndependent/glslang.y" /* yacc.c:1909 */
struct {
glslang::TSourceLoc loc;
union {
glslang::TString *string;
int i;
unsigned int u;
bool b;
double d;
};
glslang::TSymbol* symbol;
} lex;
struct {
glslang::TSourceLoc loc;
glslang::TOperator op;
union {
TIntermNode* intermNode;
glslang::TIntermNodePair nodePair;
glslang::TIntermTyped* intermTypedNode;
};
union {
glslang::TPublicType type;
glslang::TFunction* function;
glslang::TParameter param;
glslang::TTypeLoc typeLine;
glslang::TTypeList* typeList;
glslang::TArraySizes* arraySizes;
glslang::TIdentifierList* identifierList;
};
} interm;
#line 305 "glslang_tab.cpp.h" /* yacc.c:1909 */
};
typedef union YYSTYPE YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define YYSTYPE_IS_DECLARED 1
#endif
int yyparse (glslang::TParseContext* pParseContext);
#endif /* !YY_YY_GLSLANG_TAB_CPP_H_INCLUDED */

2
deps/glslang/update_yacc.sh vendored Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
bison --defines=glslang_tab.cpp.h -o glslang_tab.cpp -t glslang/glslang/MachineIndependent/glslang.y

View File

@ -55,7 +55,7 @@ struct string_list *dir_list_new_special(const char *input_dir,
break; break;
case DIR_LIST_SHADERS: case DIR_LIST_SHADERS:
dir = settings->video.shader_dir; dir = settings->video.shader_dir;
exts = "cg|cgp|glsl|glslp"; exts = "cg|cgp|glsl|glslp|slang|slangp";
break; break;
case DIR_LIST_COLLECTIONS: case DIR_LIST_COLLECTIONS:
dir = settings->playlist_directory; dir = settings->playlist_directory;

View File

@ -878,6 +878,16 @@ bool rarch_environment_cb(unsigned cmd, void *data)
RARCH_LOG("Requesting no HW context.\n"); RARCH_LOG("Requesting no HW context.\n");
break; break;
#ifdef HAVE_VULKAN
case RETRO_HW_CONTEXT_VULKAN:
RARCH_LOG("Requesting Vulkan context.\n");
break;
#else
case RETRO_HW_CONTEXT_VULKAN:
RARCH_ERR("Requesting Vulkan context, but RetroArch is not compiled against Vulkan. Cannot use HW context.\n");
return false;
#endif
#if defined(HAVE_OPENGLES2) #if defined(HAVE_OPENGLES2)
case RETRO_HW_CONTEXT_OPENGLES2: case RETRO_HW_CONTEXT_OPENGLES2:
#if defined(HAVE_OPENGLES3) #if defined(HAVE_OPENGLES3)
@ -1213,6 +1223,11 @@ bool rarch_environment_cb(unsigned cmd, void *data)
RARCH_DISPLAY_CTL_GET_CURRENT_SOFTWARE_FRAMEBUFFER, RARCH_DISPLAY_CTL_GET_CURRENT_SOFTWARE_FRAMEBUFFER,
(struct retro_framebuffer*)data); (struct retro_framebuffer*)data);
case RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE:
return video_driver_ctl(
RARCH_DISPLAY_CTL_GET_HW_RENDER_INTERFACE,
(const struct retro_hw_render_interface**)data);
/* Private extensions for internal use, not part of libretro API. */ /* Private extensions for internal use, not part of libretro API. */
/* None yet. */ /* None yet. */

756
gfx/common/vulkan_common.c Normal file
View File

@ -0,0 +1,756 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2016 - Hans-Kristian Arntzen
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "vulkan_common.h"
#include <retro_assert.h>
uint32_t vulkan_find_memory_type(const VkPhysicalDeviceMemoryProperties *mem_props,
uint32_t device_reqs, uint32_t host_reqs)
{
uint32_t i;
for (i = 0; i < VK_MAX_MEMORY_TYPES; i++)
{
if ((device_reqs & (1u << i)) &&
(mem_props->memoryTypes[i].propertyFlags & host_reqs) == host_reqs)
return i;
}
RARCH_ERR("[Vulkan]: Failed to find valid memory type. This should never happen.");
abort();
}
uint32_t vulkan_find_memory_type_fallback(const VkPhysicalDeviceMemoryProperties *mem_props,
uint32_t device_reqs, uint32_t host_reqs)
{
uint32_t i;
for (i = 0; i < VK_MAX_MEMORY_TYPES; i++)
{
if ((device_reqs & (1u << i)) &&
(mem_props->memoryTypes[i].propertyFlags & host_reqs) == host_reqs)
return i;
}
return vulkan_find_memory_type(mem_props, device_reqs, 0);
}
void vulkan_map_persistent_texture(VkDevice device, struct vk_texture *texture)
{
vkMapMemory(device, texture->memory, texture->offset, texture->size, 0, &texture->mapped);
}
#ifdef VULKAN_DEBUG_TEXTURE_ALLOC
static VkImage vk_images[4 * 1024];
static unsigned vk_count;
void vulkan_log_textures(void)
{
unsigned i;
for (i = 0; i < vk_count; i++)
{
RARCH_WARN("[Vulkan]: Found leaked texture %llu.\n",
(unsigned long long)vk_images[i]);
}
vk_count = 0;
}
static unsigned track_seq;
static void vulkan_track_alloc(VkImage image)
{
vk_images[vk_count++] = image;
RARCH_LOG("[Vulkan]: Alloc %llu (%u).\n", (unsigned long long)image, track_seq);
track_seq++;
}
static void vulkan_track_dealloc(VkImage image)
{
unsigned i;
for (i = 0; i < vk_count; i++)
{
if (image == vk_images[i])
{
vk_count--;
memmove(vk_images + i, vk_images + 1 + i, sizeof(VkImage) * (vk_count - i));
return;
}
}
retro_assert(0 && "Couldn't find VkImage in dealloc!");
}
#endif
struct vk_texture vulkan_create_texture(vk_t *vk,
struct vk_texture *old,
unsigned width, unsigned height,
VkFormat format,
const void *initial, const VkComponentMapping *swizzle, enum vk_texture_type type)
{
/* TODO: Evaluate how we should do texture uploads on discrete cards optimally.
* For integrated GPUs, using linear texture is highly desirable to avoid extra copies, but
* we might need to take a DMA transfer with block interleave on desktop GPUs.
*
* Also, Vulkan drivers are not required to support sampling from linear textures
* (only TRANSFER), but seems to work fine on GPUs I've tested so far. */
VkDevice device = vk->context->device;
struct vk_texture tex;
VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
VkImageViewCreateInfo view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
VkImageSubresource subresource = { VK_IMAGE_ASPECT_COLOR_BIT };
VkMemoryRequirements mem_reqs;
VkSubresourceLayout layout;
if (type == VULKAN_TEXTURE_STATIC && !initial)
retro_assert(0 && "Static textures must have initial data.\n");
memset(&tex, 0, sizeof(tex));
info.imageType = VK_IMAGE_TYPE_2D;
info.format = format;
info.extent.width = width;
info.extent.height = height;
info.extent.depth = 1;
info.mipLevels = 1;
info.arrayLayers = 1;
info.samples = VK_SAMPLE_COUNT_1_BIT;
info.tiling = type != VULKAN_TEXTURE_STATIC ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL;
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
if (type == VULKAN_TEXTURE_STATIC)
info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
if (type == VULKAN_TEXTURE_READBACK)
info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
/* We'll transition this on first use for streamed textures. */
info.initialLayout = type == VULKAN_TEXTURE_STREAMED ?
VK_IMAGE_LAYOUT_PREINITIALIZED :
VK_IMAGE_LAYOUT_UNDEFINED;
vkCreateImage(device, &info, NULL, &tex.image);
#if 0
vulkan_track_alloc(tex.image);
#endif
vkGetImageMemoryRequirements(device, tex.image, &mem_reqs);
alloc.allocationSize = mem_reqs.size;
if (type == VULKAN_TEXTURE_STATIC)
{
alloc.memoryTypeIndex = vulkan_find_memory_type_fallback(&vk->context->memory_properties,
mem_reqs.memoryTypeBits,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
else
{
/* This must exist. */
alloc.memoryTypeIndex = vulkan_find_memory_type(&vk->context->memory_properties,
mem_reqs.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
}
/* We're not reusing the objects themselves. */
if (old && old->view != VK_NULL_HANDLE)
vkDestroyImageView(vk->context->device, old->view, NULL);
if (old && old->image != VK_NULL_HANDLE)
{
vkDestroyImage(vk->context->device, old->image, NULL);
#ifdef VULKAN_DEBUG_TEXTURE_ALLOC
vulkan_track_dealloc(old->image);
#endif
}
/* We can pilfer the old memory and move it over to the new texture. */
if (old &&
old->memory_size >= mem_reqs.size &&
old->memory_type == alloc.memoryTypeIndex)
{
tex.memory = old->memory;
tex.memory_size = old->memory_size;
tex.memory_type = old->memory_type;
if (old->mapped)
vkUnmapMemory(device, old->memory);
old->memory = VK_NULL_HANDLE;
}
else
{
vkAllocateMemory(device, &alloc, NULL, &tex.memory);
tex.memory_size = alloc.allocationSize;
tex.memory_type = alloc.memoryTypeIndex;
}
if (old)
{
if (old->memory != VK_NULL_HANDLE)
vkFreeMemory(device, old->memory, NULL);
memset(old, 0, sizeof(*old));
}
vkBindImageMemory(device, tex.image, tex.memory, 0);
view.image = tex.image;
view.viewType = VK_IMAGE_VIEW_TYPE_2D;
view.format = format;
if (swizzle)
view.components = *swizzle;
else
{
view.components.r = VK_COMPONENT_SWIZZLE_R;
view.components.g = VK_COMPONENT_SWIZZLE_G;
view.components.b = VK_COMPONENT_SWIZZLE_B;
view.components.a = VK_COMPONENT_SWIZZLE_A;
}
view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
view.subresourceRange.levelCount = 1;
view.subresourceRange.layerCount = 1;
vkCreateImageView(device, &view, NULL, &tex.view);
vkGetImageSubresourceLayout(device, tex.image, &subresource, &layout);
tex.stride = layout.rowPitch;
tex.offset = layout.offset;
tex.size = layout.size;
tex.layout = info.initialLayout;
tex.width = width;
tex.height = height;
tex.format = format;
if (initial && type == VULKAN_TEXTURE_STREAMED)
{
unsigned bpp = vulkan_format_to_bpp(tex.format);
unsigned stride = tex.width * bpp;
unsigned x, y;
uint8_t *dst;
const uint8_t *src;
void *ptr;
vkMapMemory(device, tex.memory, tex.offset, tex.size, 0, &ptr);
dst = (uint8_t*)ptr;
src = (const uint8_t*)initial;
for (y = 0; y < tex.height; y++, dst += tex.stride, src += stride)
memcpy(dst, src, width * bpp);
vkUnmapMemory(device, tex.memory);
}
else if (initial && type == VULKAN_TEXTURE_STATIC)
{
VkCommandBufferAllocateInfo info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
VkImageCopy region;
VkCommandBuffer staging;
unsigned bpp = vulkan_format_to_bpp(tex.format);
struct vk_texture tmp = vulkan_create_texture(vk, NULL,
width, height, format, initial, NULL, VULKAN_TEXTURE_STREAMED);
info.commandPool = vk->staging_pool;
info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
info.commandBufferCount = 1;
vkAllocateCommandBuffers(vk->context->device, &info, &staging);
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(staging, &begin_info);
vulkan_image_layout_transition(vk, staging, tmp.image,
VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_GENERAL,
VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
vulkan_image_layout_transition(vk, staging, tex.image,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
0, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
memset(&region, 0, sizeof(region));
region.extent.width = width;
region.extent.height = height;
region.extent.depth = 1;
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.layerCount = 1;
region.dstSubresource = region.srcSubresource;
vkCmdCopyImage(staging,
tmp.image, VK_IMAGE_LAYOUT_GENERAL,
tex.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &region);
vulkan_image_layout_transition(vk, staging, tex.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
vkEndCommandBuffer(staging);
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &staging;
slock_lock(vk->context->queue_lock);
vkQueueSubmit(vk->context->queue, 1, &submit_info, VK_NULL_HANDLE);
/* TODO: Very crude, but texture uploads only happen during init,
* so waiting for GPU to complete transfer and blocking isn't a big deal. */
vkQueueWaitIdle(vk->context->queue);
slock_unlock(vk->context->queue_lock);
vkFreeCommandBuffers(vk->context->device, vk->staging_pool, 1, &staging);
vulkan_destroy_texture(vk->context->device, &tmp);
tex.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
return tex;
}
void vulkan_destroy_texture(VkDevice device, struct vk_texture *tex)
{
if (tex->mapped)
vkUnmapMemory(device, tex->memory);
vkFreeMemory(device, tex->memory, NULL);
vkDestroyImageView(device, tex->view, NULL);
vkDestroyImage(device, tex->image, NULL);
#ifdef VULKAN_DEBUG_TEXTURE_ALLOC
vulkan_track_dealloc(tex->image);
#endif
memset(tex, 0, sizeof(*tex));
}
static void vulkan_write_quad_descriptors(VkDevice device,
VkDescriptorSet set,
const struct vk_texture *texture,
VkSampler sampler)
{
VkWriteDescriptorSet write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
VkDescriptorImageInfo image_info;
image_info.sampler = sampler;
image_info.imageView = texture->view;
image_info.imageLayout = texture->layout;
write.dstSet = set;
write.dstBinding = 0;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write.pImageInfo = &image_info;
vkUpdateDescriptorSets(device, 1, &write, 0, NULL);
}
void vulkan_transition_texture(vk_t *vk, struct vk_texture *texture)
{
/* Transition to GENERAL layout for linear streamed textures.
* We're using linear textures here, so only GENERAL layout is supported.
*/
if (texture->layout == VK_IMAGE_LAYOUT_PREINITIALIZED)
{
vulkan_image_layout_transition(vk, vk->cmd, texture->image,
texture->layout, VK_IMAGE_LAYOUT_GENERAL,
VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
texture->layout = VK_IMAGE_LAYOUT_GENERAL;
}
}
static void vulkan_check_dynamic_state(vk_t *vk)
{
if (vk->tracker.dirty & VULKAN_DIRTY_DYNAMIC_BIT)
{
const VkRect2D sci = {{ vk->vp.x, vk->vp.y }, { vk->vp.width, vk->vp.height }};
vkCmdSetViewport(vk->cmd, 0, 1, &vk->vk_vp);
vkCmdSetScissor(vk->cmd, 0, 1, &sci);
vk->tracker.dirty &= ~VULKAN_DIRTY_DYNAMIC_BIT;
}
}
void vulkan_draw_triangles(vk_t *vk, const struct vk_draw_triangles *call)
{
vulkan_transition_texture(vk, call->texture);
if (call->pipeline != vk->tracker.pipeline)
{
vkCmdBindPipeline(vk->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, call->pipeline);
vk->tracker.pipeline = call->pipeline;
/* Changing pipeline invalidates dynamic state. */
vk->tracker.dirty |= VULKAN_DIRTY_DYNAMIC_BIT;
}
vulkan_check_dynamic_state(vk);
/* Upload descriptors */
{
struct vk_draw_uniform ubo;
VkDescriptorSet set;
ubo.mvp = *call->mvp;
ubo.texsize[0] = call->texture->width;
ubo.texsize[1] = call->texture->height;
ubo.texsize[2] = 1.0f / call->texture->width;
ubo.texsize[3] = 1.0f / call->texture->height;
if (call->texture->view != vk->tracker.view || call->sampler != vk->tracker.sampler)
{
set = vulkan_descriptor_manager_alloc(vk->context->device, &vk->chain->descriptor_manager);
vulkan_write_quad_descriptors(vk->context->device,
set,
call->texture,
call->sampler);
vkCmdBindDescriptorSets(vk->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
vk->pipelines.layout, 0,
1, &set, 0, NULL);
vk->tracker.view = call->texture->view;
vk->tracker.sampler = call->sampler;
}
vkCmdPushConstants(vk->cmd, vk->pipelines.layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
0, sizeof(struct vk_draw_uniform), &ubo);
}
/* VBO is already uploaded. */
vkCmdBindVertexBuffers(vk->cmd, 0, 1,
&call->vbo->buffer, &call->vbo->offset);
/* Draw the quad */
vkCmdDraw(vk->cmd, call->vertices, 1, 0, 0);
}
void vulkan_draw_quad(vk_t *vk, const struct vk_draw_quad *quad)
{
vulkan_transition_texture(vk, quad->texture);
if (quad->pipeline != vk->tracker.pipeline)
{
vkCmdBindPipeline(vk->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, quad->pipeline);
vk->tracker.pipeline = quad->pipeline;
/* Changing pipeline invalidates dynamic state. */
vk->tracker.dirty |= VULKAN_DIRTY_DYNAMIC_BIT;
}
vulkan_check_dynamic_state(vk);
/* Upload descriptors */
{
struct vk_draw_uniform ubo;
VkDescriptorSet set;
struct vk_buffer_range range;
if (!vulkan_buffer_chain_alloc(vk->context, &vk->chain->ubo,
sizeof(struct vk_draw_uniform), &range))
return;
ubo.mvp = *quad->mvp;
ubo.texsize[0] = quad->texture->width;
ubo.texsize[1] = quad->texture->height;
ubo.texsize[2] = 1.0f / quad->texture->width;
ubo.texsize[3] = 1.0f / quad->texture->height;
if (quad->texture->view != vk->tracker.view || quad->sampler != vk->tracker.sampler)
{
set = vulkan_descriptor_manager_alloc(vk->context->device, &vk->chain->descriptor_manager);
vulkan_write_quad_descriptors(vk->context->device,
set,
quad->texture,
quad->sampler);
vkCmdBindDescriptorSets(vk->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
vk->pipelines.layout, 0,
1, &set, 0, NULL);
vk->tracker.view = quad->texture->view;
vk->tracker.sampler = quad->sampler;
}
vkCmdPushConstants(vk->cmd, vk->pipelines.layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
0, sizeof(struct vk_draw_uniform), &ubo);
}
/* Upload VBO */
{
struct vk_buffer_range range;
if (!vulkan_buffer_chain_alloc(vk->context, &vk->chain->vbo,
6 * sizeof(struct vk_vertex), &range))
return;
vulkan_write_quad_vbo((struct vk_vertex*)range.data,
0.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
&quad->color);
vkCmdBindVertexBuffers(vk->cmd, 0, 1,
&range.buffer, &range.offset);
}
/* Draw the quad */
vkCmdDraw(vk->cmd, 6, 1, 0, 0);
}
void vulkan_image_layout_transition(vk_t *vk, VkCommandBuffer cmd, VkImage image,
VkImageLayout old_layout, VkImageLayout new_layout,
VkAccessFlags srcAccess, VkAccessFlags dstAccess,
VkPipelineStageFlags srcStages, VkPipelineStageFlags dstStages)
{
VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
barrier.srcAccessMask = srcAccess;
barrier.dstAccessMask = dstAccess;
barrier.oldLayout = old_layout;
barrier.newLayout = new_layout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(cmd,
srcStages,
dstStages,
0,
0, NULL,
0, NULL,
1, &barrier);
}
struct vk_buffer vulkan_create_buffer(const struct vulkan_context *context,
size_t size, VkBufferUsageFlags usage)
{
struct vk_buffer buffer;
VkMemoryRequirements mem_reqs;
VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
VkBufferCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
info.size = size;
info.usage = usage;
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vkCreateBuffer(context->device, &info, NULL, &buffer.buffer);
vkGetBufferMemoryRequirements(context->device, buffer.buffer, &mem_reqs);
alloc.allocationSize = mem_reqs.size;
alloc.memoryTypeIndex = vulkan_find_memory_type(&context->memory_properties,
mem_reqs.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
vkAllocateMemory(context->device, &alloc, NULL, &buffer.memory);
vkBindBufferMemory(context->device, buffer.buffer, buffer.memory, 0);
buffer.size = alloc.allocationSize;
vkMapMemory(context->device, buffer.memory, 0, buffer.size, 0, &buffer.mapped);
return buffer;
}
void vulkan_destroy_buffer(VkDevice device, struct vk_buffer *buffer)
{
vkUnmapMemory(device, buffer->memory);
vkFreeMemory(device, buffer->memory, NULL);
vkDestroyBuffer(device, buffer->buffer, NULL);
memset(buffer, 0, sizeof(*buffer));
}
static struct vk_descriptor_pool *vulkan_alloc_descriptor_pool(VkDevice device,
const struct vk_descriptor_manager *manager)
{
unsigned i;
VkDescriptorPoolCreateInfo pool_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
VkDescriptorSetAllocateInfo alloc_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
struct vk_descriptor_pool *pool = (struct vk_descriptor_pool*)calloc(1, sizeof(*pool));
if (!pool)
return NULL;
pool_info.maxSets = VULKAN_DESCRIPTOR_MANAGER_BLOCK_SETS;
pool_info.poolSizeCount = manager->num_sizes;
pool_info.pPoolSizes = manager->sizes;
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
vkCreateDescriptorPool(device, &pool_info, NULL, &pool->pool);
/* Just allocate all descriptor sets up front. */
alloc_info.descriptorPool = pool->pool;
alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &manager->set_layout;
for (i = 0; i < VULKAN_DESCRIPTOR_MANAGER_BLOCK_SETS; i++)
vkAllocateDescriptorSets(device, &alloc_info, &pool->sets[i]);
return pool;
}
VkDescriptorSet vulkan_descriptor_manager_alloc(VkDevice device, struct vk_descriptor_manager *manager)
{
if (manager->count < VULKAN_DESCRIPTOR_MANAGER_BLOCK_SETS)
return manager->current->sets[manager->count++];
while (manager->current->next)
{
manager->current = manager->current->next;
manager->count = 0;
return manager->current->sets[manager->count++];
}
manager->current->next = vulkan_alloc_descriptor_pool(device, manager);
retro_assert(manager->current->next);
manager->current = manager->current->next;
manager->count = 0;
return manager->current->sets[manager->count++];
}
void vulkan_descriptor_manager_restart(struct vk_descriptor_manager *manager)
{
manager->current = manager->head;
manager->count = 0;
}
struct vk_descriptor_manager vulkan_create_descriptor_manager(VkDevice device,
const VkDescriptorPoolSize *sizes, unsigned num_sizes, VkDescriptorSetLayout set_layout)
{
struct vk_descriptor_manager manager;
memset(&manager, 0, sizeof(manager));
retro_assert(num_sizes <= VULKAN_MAX_DESCRIPTOR_POOL_SIZES);
memcpy(manager.sizes, sizes, num_sizes * sizeof(*sizes));
manager.num_sizes = num_sizes;
manager.set_layout = set_layout;
manager.head = vulkan_alloc_descriptor_pool(device, &manager);
retro_assert(manager.head);
return manager;
}
void vulkan_destroy_descriptor_manager(VkDevice device, struct vk_descriptor_manager *manager)
{
struct vk_descriptor_pool *node = manager->head;
while (node)
{
struct vk_descriptor_pool *next = node->next;
vkFreeDescriptorSets(device, node->pool, VULKAN_DESCRIPTOR_MANAGER_BLOCK_SETS, node->sets);
vkDestroyDescriptorPool(device, node->pool, NULL);
free(node);
node = next;
}
memset(manager, 0, sizeof(*manager));
}
static void vulkan_buffer_chain_step(struct vk_buffer_chain *chain)
{
chain->current = chain->current->next;
chain->offset = 0;
}
static bool vulkan_buffer_chain_suballoc(struct vk_buffer_chain *chain, size_t size, struct vk_buffer_range *range)
{
VkDeviceSize next_offset = chain->offset + size;
if (next_offset <= chain->current->buffer.size)
{
range->data = (uint8_t*)chain->current->buffer.mapped + chain->offset;
range->buffer = chain->current->buffer.buffer;
range->offset = chain->offset;
chain->offset = (next_offset + chain->alignment - 1) & ~(chain->alignment - 1);
return true;
}
else
return false;
}
static struct vk_buffer_node *vulkan_buffer_chain_alloc_node(
const struct vulkan_context *context,
size_t size, VkBufferUsageFlags usage)
{
struct vk_buffer_node *node = (struct vk_buffer_node*)calloc(1, sizeof(*node));
if (!node)
return NULL;
node->buffer = vulkan_create_buffer(context, size, usage);
return node;
}
struct vk_buffer_chain vulkan_buffer_chain_init(VkDeviceSize block_size,
VkDeviceSize alignment,
VkBufferUsageFlags usage)
{
struct vk_buffer_chain chain = { block_size, alignment, 0, usage, NULL, NULL };
return chain;
}
void vulkan_buffer_chain_discard(struct vk_buffer_chain *chain)
{
chain->current = chain->head;
chain->offset = 0;
}
bool vulkan_buffer_chain_alloc(const struct vulkan_context *context,
struct vk_buffer_chain *chain, size_t size, struct vk_buffer_range *range)
{
if (!chain->head)
{
chain->head = vulkan_buffer_chain_alloc_node(context,
chain->block_size, chain->usage);
if (!chain->head)
return false;
chain->current = chain->head;
chain->offset = 0;
}
if (vulkan_buffer_chain_suballoc(chain, size, range))
return true;
/* We've exhausted the current chain, traverse list until we
* can find a block we can use. Usually, we just step once. */
while (chain->current->next)
{
vulkan_buffer_chain_step(chain);
if (vulkan_buffer_chain_suballoc(chain, size, range))
return true;
}
/* We have to allocate a new node, might allocate larger
* buffer here than block_size in case we have a very large allocation. */
if (size < chain->block_size)
size = chain->block_size;
chain->current->next = vulkan_buffer_chain_alloc_node(
context, size, chain->usage);
if (!chain->current->next)
return false;
vulkan_buffer_chain_step(chain);
/* This cannot possibly fail. */
retro_assert(vulkan_buffer_chain_suballoc(chain, size, range));
return true;
}
void vulkan_buffer_chain_free(VkDevice device, struct vk_buffer_chain *chain)
{
struct vk_buffer_node *node = chain->head;
while (node)
{
struct vk_buffer_node *next = node->next;
vulkan_destroy_buffer(device, &node->buffer);
free(node);
node = next;
}
memset(chain, 0, sizeof(*chain));
}

412
gfx/common/vulkan_common.h Normal file
View File

@ -0,0 +1,412 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2016 - Hans-Kristian Arntzen
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef VULKAN_COMMON_H__
#define VULKAN_COMMON_H__
#define VK_PROTOTYPES
#include <vulkan/vulkan.h>
#include <gfx/math/matrix_4x4.h>
#include <formats/image.h>
#include <retro_inline.h>
#include <retro_miscellaneous.h>
#include "boolean.h"
#include "../../driver.h"
#include "../../performance.h"
#include "../../libretro.h"
#include "../../general.h"
#include "../../retroarch.h"
#include "../font_driver.h"
#include "../video_context_driver.h"
#include "libretro_vulkan.h"
#include "../drivers_shader/shader_vulkan.h"
#include <rthreads/rthreads.h>
#include <gfx/scaler/scaler.h>
#define VULKAN_MAX_SWAPCHAIN_IMAGES 8
#define VULKAN_DIRTY_DYNAMIC_BIT 0x0001
typedef struct vulkan_context
{
VkInstance instance;
VkPhysicalDevice gpu;
VkDevice device;
VkQueue queue;
uint32_t graphics_queue_index;
VkPhysicalDeviceProperties gpu_properties;
VkPhysicalDeviceMemoryProperties memory_properties;
bool invalid_swapchain;
VkImage swapchain_images[VULKAN_MAX_SWAPCHAIN_IMAGES];
VkFence swapchain_fences[VULKAN_MAX_SWAPCHAIN_IMAGES];
VkSemaphore swapchain_semaphores[VULKAN_MAX_SWAPCHAIN_IMAGES];
uint32_t num_swapchain_images;
uint32_t current_swapchain_index;
unsigned swapchain_width;
unsigned swapchain_height;
VkFormat swapchain_format;
slock_t *queue_lock;
/* Used by screenshot to get blits with correct colorspace. */
bool swapchain_is_srgb;
} vulkan_context_t;
struct vk_draw_uniform
{
math_matrix_4x4 mvp;
float texsize[4];
};
struct vk_color
{
float r, g, b, a;
};
struct vk_vertex
{
float x, y;
float tex_x, tex_y;
struct vk_color color;
};
struct vk_image
{
VkImage image;
VkImageView view;
VkFramebuffer framebuffer;
};
struct vk_texture
{
VkImage image;
VkImageView view;
VkDeviceMemory memory;
unsigned width, height;
VkFormat format;
void *mapped;
size_t offset;
size_t stride;
size_t size;
VkDeviceSize memory_size;
uint32_t memory_type;
VkImageLayout layout;
bool default_smooth;
};
struct vk_buffer
{
VkBuffer buffer;
VkDeviceMemory memory;
VkDeviceSize size;
void *mapped;
};
struct vk_buffer_node
{
struct vk_buffer buffer;
struct vk_buffer_node *next;
};
struct vk_buffer_chain
{
VkDeviceSize block_size;
VkDeviceSize alignment;
VkDeviceSize offset;
VkBufferUsageFlags usage;
struct vk_buffer_node *head;
struct vk_buffer_node *current;
};
struct vk_buffer_range
{
uint8_t *data;
VkBuffer buffer;
VkDeviceSize offset;
};
struct vk_buffer_chain vulkan_buffer_chain_init(
VkDeviceSize block_size,
VkDeviceSize alignment,
VkBufferUsageFlags usage);
void vulkan_buffer_chain_discard(struct vk_buffer_chain *chain);
bool vulkan_buffer_chain_alloc(const struct vulkan_context *context,
struct vk_buffer_chain *chain, size_t size, struct vk_buffer_range *range);
void vulkan_buffer_chain_free(VkDevice device, struct vk_buffer_chain *chain);
#define VULKAN_DESCRIPTOR_MANAGER_BLOCK_SETS 16
#define VULKAN_BUFFER_BLOCK_SIZE (4 * 1024)
#define VULKAN_MAX_DESCRIPTOR_POOL_SIZES 16
struct vk_descriptor_pool
{
VkDescriptorPool pool;
VkDescriptorSet sets[VULKAN_DESCRIPTOR_MANAGER_BLOCK_SETS];
struct vk_descriptor_pool *next;
};
struct vk_descriptor_manager
{
struct vk_descriptor_pool *head;
struct vk_descriptor_pool *current;
unsigned count;
VkDescriptorPoolSize sizes[VULKAN_MAX_DESCRIPTOR_POOL_SIZES];
VkDescriptorSetLayout set_layout;
unsigned num_sizes;
};
struct vk_per_frame
{
struct vk_image backbuffer;
struct vk_texture texture;
struct vk_buffer_chain vbo;
struct vk_buffer_chain ubo;
struct vk_descriptor_manager descriptor_manager;
VkCommandPool cmd_pool;
VkCommandBuffer cmd;
};
struct vk_draw_quad
{
VkPipeline pipeline;
struct vk_texture *texture;
VkSampler sampler;
const math_matrix_4x4 *mvp;
struct vk_color color;
};
struct vk_draw_triangles
{
VkPipeline pipeline;
struct vk_texture *texture;
VkSampler sampler;
const math_matrix_4x4 *mvp;
const struct vk_buffer_range *vbo;
unsigned vertices;
};
typedef struct vk
{
vulkan_context_t *context;
video_info_t video;
unsigned full_x, full_y;
unsigned tex_w, tex_h;
VkFormat tex_fmt;
bool vsync;
bool keep_aspect;
bool fullscreen;
bool quitting;
bool should_resize;
unsigned vp_out_width, vp_out_height;
unsigned rotation;
math_matrix_4x4 mvp, mvp_no_rot;
struct video_viewport vp;
VkViewport vk_vp;
VkRenderPass render_pass;
struct vk_per_frame swapchain[VULKAN_MAX_SWAPCHAIN_IMAGES];
unsigned num_swapchain_images;
VkCommandBuffer cmd; /* Currently active command buffer. */
VkCommandPool staging_pool; /* Staging pool for doing buffer transfers on GPU. */
struct
{
struct vk_texture staging[VULKAN_MAX_SWAPCHAIN_IMAGES];
struct scaler_ctx scaler;
bool pending;
bool streamed;
} readback;
struct
{
struct vk_texture *images;
struct vk_vertex *vertex;
unsigned count;
bool enable;
bool full_screen;
} overlay;
struct
{
VkPipeline alpha_blend;
VkDescriptorSetLayout set_layout;
VkPipelineLayout layout;
VkPipelineCache cache;
} pipelines;
struct
{
bool blend;
VkPipeline pipelines[4];
struct vk_texture blank_texture;
} display;
struct
{
struct vk_texture textures[VULKAN_MAX_SWAPCHAIN_IMAGES];
float alpha;
unsigned last_index;
bool enable;
bool full_screen;
} menu;
struct
{
VkSampler linear;
VkSampler nearest;
} samplers;
unsigned last_valid_index;
struct vk_per_frame *chain;
struct
{
struct retro_hw_render_interface_vulkan iface;
const struct retro_vulkan_image *image;
const VkSemaphore *semaphores;
uint32_t num_semaphores;
VkPipelineStageFlags *wait_dst_stages;
VkCommandBuffer *cmd;
uint32_t num_cmd;
unsigned capacity_cmd;
unsigned last_width;
unsigned last_height;
bool enable;
} hw;
struct
{
VkPipeline pipeline;
VkImageView view;
VkSampler sampler;
uint64_t dirty;
} tracker;
vulkan_filter_chain_t *filter_chain;
} vk_t;
uint32_t vulkan_find_memory_type(const VkPhysicalDeviceMemoryProperties *mem_props,
uint32_t device_reqs, uint32_t host_reqs);
uint32_t vulkan_find_memory_type_fallback(const VkPhysicalDeviceMemoryProperties *mem_props,
uint32_t device_reqs, uint32_t host_reqs);
enum vk_texture_type
{
VULKAN_TEXTURE_STREAMED = 0,
VULKAN_TEXTURE_STATIC,
VULKAN_TEXTURE_READBACK
};
struct vk_texture vulkan_create_texture(vk_t *vk,
struct vk_texture *old,
unsigned width, unsigned height,
VkFormat format,
const void *initial, const VkComponentMapping *swizzle,
enum vk_texture_type type);
void vulkan_transition_texture(vk_t *vk, struct vk_texture *texture);
void vulkan_map_persistent_texture(VkDevice device, struct vk_texture *tex);
void vulkan_destroy_texture(VkDevice device, struct vk_texture *tex);
/* VBO will be written to here. */
void vulkan_draw_quad(vk_t *vk, const struct vk_draw_quad *quad);
/* The VBO needs to be written to before calling this.
* Use vulkan_buffer_chain_alloc.
*/
void vulkan_draw_triangles(vk_t *vk, const struct vk_draw_triangles *call);
void vulkan_image_layout_transition(vk_t *vk, VkCommandBuffer cmd, VkImage image,
VkImageLayout old_layout, VkImageLayout new_layout,
VkAccessFlags srcAccess, VkAccessFlags dstAccess,
VkPipelineStageFlags srcStages, VkPipelineStageFlags dstStages);
static inline unsigned vulkan_format_to_bpp(VkFormat format)
{
switch (format)
{
case VK_FORMAT_B8G8R8A8_UNORM:
return 4;
case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
case VK_FORMAT_R5G6B5_UNORM_PACK16:
return 2;
case VK_FORMAT_R8_UNORM:
return 1;
default:
RARCH_ERR("[Vulkan]: Unknown format.\n");
abort();
}
}
static inline void vulkan_write_quad_vbo(struct vk_vertex *pv,
float x, float y, float width, float height,
float tex_x, float tex_y, float tex_width, float tex_height,
const struct vk_color *color)
{
unsigned i;
static const float strip[2 * 6] = {
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 1.0f,
};
for (i = 0; i < 6; i++)
{
pv[i].x = x + strip[2 * i + 0] * width;
pv[i].y = y + strip[2 * i + 1] * height;
pv[i].tex_x = tex_x + strip[2 * i + 0] * tex_width;
pv[i].tex_y = tex_y + strip[2 * i + 1] * tex_height;
pv[i].color = *color;
}
}
struct vk_buffer vulkan_create_buffer(const struct vulkan_context *context,
size_t size, VkBufferUsageFlags usage);
void vulkan_destroy_buffer(VkDevice device, struct vk_buffer *buffer);
VkDescriptorSet vulkan_descriptor_manager_alloc(VkDevice device, struct vk_descriptor_manager *manager);
void vulkan_descriptor_manager_restart(struct vk_descriptor_manager *manager);
struct vk_descriptor_manager vulkan_create_descriptor_manager(VkDevice device,
const VkDescriptorPoolSize *sizes, unsigned num_sizes,
VkDescriptorSetLayout set_layout);
void vulkan_destroy_descriptor_manager(VkDevice device,
struct vk_descriptor_manager *manager);
#endif

View File

@ -3850,13 +3850,14 @@ static uintptr_t gl_load_texture(void *video_data, void *data,
return id; return id;
} }
static void gl_unload_texture(void *data, uintptr_t *id) static void gl_unload_texture(void *data, uintptr_t id)
{ {
GLuint glid;
if (!id) if (!id)
return; return;
glDeleteTextures(1, (const GLuint*)id); glid = (GLuint)id;
*id = 0; glDeleteTextures(1, &glid);
} }
static const video_poke_interface_t gl_poke_interface = { static const video_poke_interface_t gl_poke_interface = {

View File

@ -19,6 +19,7 @@
#include <retro_assert.h> #include <retro_assert.h>
#include <gfx/scaler/scaler.h> #include <gfx/scaler/scaler.h>
#include <retro_assert.h>
#include "SDL.h" #include "SDL.h"

2011
gfx/drivers/vulkan.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
VERT_SHADERS := $(wildcard *.vert)
FRAG_SHADERS := $(wildcard *.frag)
SPIRV := $(VERT_SHADERS:.vert=.vert.spv) $(FRAG_SHADERS:.frag=.frag.spv)
INCLUDES := $(SPIRV:.spv=.inc)
GLSLANG := glslangValidator
all: $(INCLUDES)
%.frag.spv: %.frag
$(GLSLANG) -V -o $@ $<
%.vert.spv: %.vert
$(GLSLANG) -V -o $@ $<
%.inc: %.spv
xxd -i $< $@
clean:
rm -f $(INCLUDES)
rm -f $(SPIRV)
.PHONY: clean

View File

@ -0,0 +1,11 @@
#version 310 es
precision highp float;
layout(location = 0) in vec2 vTexCoord;
layout(location = 1) in vec4 vColor;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 0) uniform highp sampler2D uTex;
void main()
{
FragColor = vColor * texture(uTex, vTexCoord);
}

View File

@ -0,0 +1,59 @@
unsigned char alpha_blend_frag_spv[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x03, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00,
0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00,
0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00,
0x05, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x76, 0x43, 0x6f, 0x6c,
0x6f, 0x72, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x75, 0x54, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
0x14, 0x00, 0x00, 0x00, 0x76, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72,
0x64, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00,
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00,
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x0d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00,
0x05, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x3d, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00
};
unsigned int alpha_blend_frag_spv_len = 664;

View File

@ -0,0 +1,19 @@
#version 310 es
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 2) in vec4 Color;
layout(location = 0) out vec2 vTexCoord;
layout(location = 1) out vec4 vColor;
layout(push_constant, std140) uniform UBO
{
mat4 MVP;
vec4 texsize;
} global;
void main()
{
gl_Position = global.MVP * Position;
vTexCoord = TexCoord;
vColor = Color;
}

View File

@ -0,0 +1,113 @@
unsigned char alpha_blend_vert_spv[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
0x01, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50,
0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00,
0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x55, 0x42, 0x4f, 0x00, 0x06, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4d, 0x56, 0x50, 0x00, 0x06, 0x00, 0x05, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x73,
0x69, 0x7a, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
0x15, 0x00, 0x00, 0x00, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x76, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x00, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x54, 0x65, 0x78, 0x43,
0x6f, 0x6f, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x20, 0x00, 0x00, 0x00, 0x76, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00,
0x05, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6c, 0x6f,
0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x24, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x5f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x44, 0x00,
0x05, 0x00, 0x06, 0x00, 0x25, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x49,
0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x24, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x0f, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
0x1a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x1a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x1d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x23, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00,
0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00,
0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x3e, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x3d, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x1f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
0x20, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00
};
unsigned int alpha_blend_vert_spv_len = 1312;

View File

@ -0,0 +1,19 @@
#version 310 es
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 2) in vec4 Color;
layout(location = 0) out vec2 vTexCoord;
layout(location = 1) out vec4 vColor;
layout(push_constant, std140) uniform UBO
{
mat4 MVP;
vec4 texsize;
} global;
void main()
{
gl_Position = global.MVP * Position;
vTexCoord = TexCoord;
vColor = Color;
}

View File

@ -0,0 +1,113 @@
unsigned char font_vert_spv[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
0x01, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50,
0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00,
0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x55, 0x42, 0x4f, 0x00, 0x06, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4d, 0x56, 0x50, 0x00, 0x06, 0x00, 0x05, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x73,
0x69, 0x7a, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
0x15, 0x00, 0x00, 0x00, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x76, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x00, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x54, 0x65, 0x78, 0x43,
0x6f, 0x6f, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x20, 0x00, 0x00, 0x00, 0x76, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00,
0x05, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6c, 0x6f,
0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x24, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x5f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x44, 0x00,
0x05, 0x00, 0x06, 0x00, 0x25, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x49,
0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x24, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x0f, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
0x1a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x1a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x1d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x23, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00,
0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00,
0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x3e, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x3d, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x1f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
0x20, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00
};
unsigned int font_vert_spv_len = 1312;

View File

@ -0,0 +1,10 @@
#version 310 es
precision highp float;
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform highp sampler2D Source;
void main()
{
FragColor = vec4(texture(Source, vTexCoord).rgb, 1.0);
}

View File

@ -0,0 +1,63 @@
unsigned char opaque_frag_spv[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
0x01, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x46, 0x72, 0x61, 0x67,
0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x0d, 0x00, 0x00, 0x00, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x76, 0x54, 0x65, 0x78,
0x43, 0x6f, 0x6f, 0x72, 0x64, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x11, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00,
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00,
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x03, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00,
0x05, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00,
0x0f, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x08, 0x00,
0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
0x17, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00,
0x1a, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
0x09, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00
};
unsigned int opaque_frag_spv_len = 712;

View File

@ -0,0 +1,15 @@
#version 310 es
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;
layout(std140, set = 0, binding = 0) uniform UBO
{
mat4 MVP;
} global;
void main()
{
gl_Position = global.MVP * Position;
vTexCoord = TexCoord;
}

View File

@ -0,0 +1,99 @@
unsigned char opaque_vert_spv[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x03, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00,
0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x5f, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74,
0x69, 0x6f, 0x6e, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x69, 0x6e, 0x74,
0x53, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x55, 0x42, 0x4f, 0x00, 0x06, 0x00, 0x04, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x56, 0x50, 0x00,
0x05, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x6f, 0x62,
0x61, 0x6c, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00,
0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x76, 0x54, 0x65, 0x78,
0x43, 0x6f, 0x6f, 0x72, 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x5f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x44, 0x00,
0x05, 0x00, 0x06, 0x00, 0x22, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x49,
0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x47, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x15, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x21, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x0d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x0d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x1b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x20, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00,
0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00,
0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x3e, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x3d, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x1f, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00
};
unsigned int opaque_vert_spv_len = 1152;

View File

@ -452,7 +452,7 @@ static bool gfx_ctx_wl_set_video_mode(void *data,
wl->win = wl_egl_window_create(wl->surface, wl->width, wl->height); wl->win = wl_egl_window_create(wl->surface, wl->width, wl->height);
wl->shell_surf = wl_shell_get_shell_surface(wl->shell, wl->surface); wl->shell_surf = wl_shell_get_shell_surface(wl->shell, wl->surface);
wl_shell_surface_add_listener(wl->shell_surf, &shell_surface_listener, NULL); wl_shell_surface_add_listener(wl->shell_surf, &shell_surface_listener, wl);
wl_shell_surface_set_toplevel(wl->shell_surf); wl_shell_surface_set_toplevel(wl->shell_surf);
wl_shell_surface_set_class(wl->shell_surf, "RetroArch"); wl_shell_surface_set_class(wl->shell_surf, "RetroArch");
wl_shell_surface_set_title(wl->shell_surf, "RetroArch"); wl_shell_surface_set_title(wl->shell_surf, "RetroArch");

View File

@ -0,0 +1,824 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2016 - Hans-Kristian Arntzen
* Copyright (C) 2011-2015 - Daniel De Matteis
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#define VK_USE_PLATFORM_WAYLAND_KHR
#include "../common/vulkan_common.h"
#include "../video_context_driver.h"
#include <sys/poll.h>
#include <unistd.h>
#include <signal.h>
#include <wayland-client.h>
#include "../../driver.h"
#include "../../general.h"
#include "../../runloop.h"
static volatile sig_atomic_t g_quit = 0;
static VkInstance cached_instance;
static VkDevice cached_device;
static void sighandler(int sig)
{
(void)sig;
g_quit = 1;
}
static void install_sighandlers(void)
{
struct sigaction sa;
sa.sa_sigaction = NULL;
sa.sa_handler = sighandler;
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
}
#define GET_INSTANCE_PROC_ADDR(inst, entrypoint) do { \
wl->fp##entrypoint = (PFN_vk##entrypoint) vkGetInstanceProcAddr(inst, "vk"#entrypoint); \
if (wl->fp##entrypoint == NULL) { \
RARCH_ERR("vkGetInstanceProcAddr failed to find vk%s\n", #entrypoint); \
goto error; \
} \
} while(0)
#define GET_DEVICE_PROC_ADDR(dev, entrypoint) do { \
wl->fp##entrypoint = (PFN_vk##entrypoint) vkGetDeviceProcAddr(dev, "vk" #entrypoint); \
if (wl->fp##entrypoint == NULL) { \
RARCH_ERR("vkGetDeviceProcAddr failed to find vk%s\n", #entrypoint); \
goto error; \
} \
} while(0)
typedef struct gfx_ctx_wayland_data
{
struct vulkan_context context;
bool resize;
int fd;
unsigned width;
unsigned height;
struct wl_display *dpy;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_surface *surf;
struct wl_shell_surface *shell_surf;
struct wl_shell *shell;
unsigned swap_interval;
bool need_new_swapchain;
unsigned buffer_scale;
PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR;
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR;
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR;
PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR;
PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR;
PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR;
PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR;
PFN_vkQueuePresentKHR fpQueuePresentKHR;
PFN_vkCreateWaylandSurfaceKHR fpCreateWaylandSurfaceKHR;
PFN_vkDestroySurfaceKHR fpDestroySurfaceKHR;
VkSurfaceKHR surface;
VkSwapchainKHR swapchain;
} gfx_ctx_wayland_data_t;
static bool vulkan_create_swapchain(gfx_ctx_wayland_data_t *wl);
/* Shell surface callbacks. */
static void shell_surface_handle_ping(void *data,
struct wl_shell_surface *shell_surface,
uint32_t serial)
{
(void)data;
wl_shell_surface_pong(shell_surface, serial);
}
static void shell_surface_handle_configure(void *data,
struct wl_shell_surface *shell_surface,
uint32_t edges, int32_t width, int32_t height)
{
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
(void)shell_surface;
(void)edges;
wl->width = wl->buffer_scale * width;
wl->height = wl->buffer_scale * height;
RARCH_LOG("[Wayland/Vulkan]: Surface configure: %u x %u.\n",
wl->width, wl->height);
}
static void shell_surface_handle_popup_done(void *data,
struct wl_shell_surface *shell_surface)
{
(void)data;
(void)shell_surface;
}
static const struct wl_shell_surface_listener shell_surface_listener = {
shell_surface_handle_ping,
shell_surface_handle_configure,
shell_surface_handle_popup_done,
};
/* Registry callbacks. */
static void registry_handle_global(void *data, struct wl_registry *reg,
uint32_t id, const char *interface, uint32_t version)
{
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
(void)version;
if (!strcmp(interface, "wl_compositor"))
wl->compositor = (struct wl_compositor*)wl_registry_bind(reg, id, &wl_compositor_interface, 3);
else if (!strcmp(interface, "wl_shell"))
wl->shell = (struct wl_shell*)wl_registry_bind(reg, id, &wl_shell_interface, 1);
}
static void registry_handle_global_remove(void *data,
struct wl_registry *registry, uint32_t id)
{
(void)data;
(void)registry;
(void)id;
}
static const struct wl_registry_listener registry_listener = {
registry_handle_global,
registry_handle_global_remove,
};
static void gfx_ctx_wl_get_video_size(void *data,
unsigned *width, unsigned *height);
static void gfx_ctx_wl_destroy_resources(gfx_ctx_wayland_data_t *wl)
{
unsigned i;
if (!wl)
return;
if (wl->context.queue)
vkQueueWaitIdle(wl->context.queue);
if (wl->swapchain)
wl->fpDestroySwapchainKHR(wl->context.device, wl->swapchain, NULL);
if (wl->surface)
wl->fpDestroySurfaceKHR(wl->context.instance, wl->surface, NULL);
for (i = 0; i < VULKAN_MAX_SWAPCHAIN_IMAGES; i++)
{
if (wl->context.swapchain_semaphores[i] != VK_NULL_HANDLE)
vkDestroySemaphore(wl->context.device, wl->context.swapchain_semaphores[i], NULL);
if (wl->context.swapchain_fences[i] != VK_NULL_HANDLE)
vkDestroyFence(wl->context.device, wl->context.swapchain_fences[i], NULL);
}
if (video_driver_ctl(RARCH_DISPLAY_CTL_IS_VIDEO_CACHE_CONTEXT, NULL))
{
cached_device = wl->context.device;
cached_instance = wl->context.instance;
}
else
{
if (wl->context.device)
vkDestroyDevice(wl->context.device, NULL);
if (wl->context.instance)
vkDestroyInstance(wl->context.instance, NULL);
}
if (wl->fd >= 0)
close(wl->fd);
if (wl->shell_surf)
wl_shell_surface_destroy(wl->shell_surf);
if (wl->surf)
wl_surface_destroy(wl->surf);
if (wl->compositor)
wl_compositor_destroy(wl->compositor);
if (wl->registry)
wl_registry_destroy(wl->registry);
if (wl->dpy)
wl_display_disconnect(wl->dpy);
wl->dpy = NULL;
wl->shell = NULL;
wl->compositor = NULL;
wl->registry = NULL;
wl->dpy = NULL;
wl->shell_surf = NULL;
wl->surf = NULL;
wl->width = 0;
wl->height = 0;
}
static void flush_wayland_fd(gfx_ctx_wayland_data_t *wl)
{
struct pollfd fd = {0};
wl_display_dispatch_pending(wl->dpy);
wl_display_flush(wl->dpy);
fd.fd = wl->fd;
fd.events = POLLIN | POLLOUT | POLLERR | POLLHUP;
if (poll(&fd, 1, 0) > 0)
{
if (fd.revents & (POLLERR | POLLHUP))
{
close(wl->fd);
g_quit = true;
}
if (fd.revents & POLLIN)
wl_display_dispatch(wl->dpy);
if (fd.revents & POLLOUT)
wl_display_flush(wl->dpy);
}
}
static void gfx_ctx_wl_check_window(void *data, bool *quit,
bool *resize, unsigned *width, unsigned *height,
unsigned frame_count)
{
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
unsigned new_width, new_height;
(void)frame_count;
flush_wayland_fd(wl);
new_width = *width;
new_height = *height;
gfx_ctx_wl_get_video_size(wl, &new_width, &new_height);
/* Swapchains are recreated in set_resize as a central place, so use that to trigger swapchain reinit. */
*resize = wl->need_new_swapchain;
if (new_width != *width || new_height != *height)
{
*resize = true;
*width = new_width;
*height = new_height;
}
*quit = g_quit;
}
static bool gfx_ctx_wl_set_resize(void *data, unsigned width, unsigned height)
{
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
wl->width = width;
wl->height = height;
if (!vulkan_create_swapchain(wl))
{
RARCH_ERR("[Wayland/Vulkan]: Failed to update swapchain.\n");
return false;
}
else
wl->context.invalid_swapchain = true;
wl->need_new_swapchain = false;
return true;
}
static void gfx_ctx_wl_update_window_title(void *data)
{
char buf[128] = {0};
char buf_fps[128] = {0};
settings_t *settings = config_get_ptr();
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
if (video_monitor_get_fps(buf, sizeof(buf),
buf_fps, sizeof(buf_fps)))
wl_shell_surface_set_title(wl->shell_surf, buf);
if (settings->fps_show)
runloop_msg_queue_push(buf_fps, 1, 1, false);
}
static void gfx_ctx_wl_get_video_size(void *data,
unsigned *width, unsigned *height)
{
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
*width = wl->width;
*height = wl->height;
}
#define DEFAULT_WINDOWED_WIDTH 640
#define DEFAULT_WINDOWED_HEIGHT 480
static void *gfx_ctx_wl_init(void *video_driver)
{
VkApplicationInfo app = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
VkDeviceQueueCreateInfo queue_info = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
VkPhysicalDeviceFeatures features = { false };
VkDeviceCreateInfo device_info = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
uint32_t gpu_count = 1;
uint32_t queue_count;
bool found_queue = false;
unsigned i;
VkQueueFamilyProperties queue_properties[32];
static const float one = 1.0f;
static const char *instance_extensions[] = {
"VK_KHR_surface",
"VK_KHR_wayland_surface",
};
static const char *device_extensions[] = {
"VK_KHR_swapchain",
};
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)
calloc(1, sizeof(gfx_ctx_wayland_data_t));
if (!wl)
return NULL;
wl->dpy = wl_display_connect(NULL);
if (!wl->dpy)
{
RARCH_ERR("Failed to connect to Wayland server.\n");
goto error;
}
install_sighandlers();
wl->registry = wl_display_get_registry(wl->dpy);
wl_registry_add_listener(wl->registry, &registry_listener, wl);
wl_display_roundtrip(wl->dpy);
if (!wl->compositor)
{
RARCH_ERR("Failed to create compositor.\n");
goto error;
}
if (!wl->shell)
{
RARCH_ERR("Failed to create shell.\n");
goto error;
}
wl->fd = wl_display_get_fd(wl->dpy);
app.pApplicationName = "RetroArch";
app.applicationVersion = 0;
app.pEngineName = "RetroArch";
app.engineVersion = 0;
app.apiVersion = VK_API_VERSION;
info.pApplicationInfo = &app;
info.enabledExtensionCount = ARRAY_SIZE(instance_extensions);
info.ppEnabledExtensionNames = instance_extensions;
if (cached_instance)
{
wl->context.instance = cached_instance;
cached_instance = NULL;
}
else if (vkCreateInstance(&info, NULL, &wl->context.instance) != VK_SUCCESS)
goto error;
if (vkEnumeratePhysicalDevices(wl->context.instance, &gpu_count, &wl->context.gpu) != VK_SUCCESS)
goto error;
if (gpu_count != 1)
{
RARCH_ERR("[Wayland/Vulkan]: Failed to enumerate Vulkan physical device.\n");
goto error;
}
vkGetPhysicalDeviceProperties(wl->context.gpu, &wl->context.gpu_properties);
vkGetPhysicalDeviceMemoryProperties(wl->context.gpu, &wl->context.memory_properties);
vkGetPhysicalDeviceQueueFamilyProperties(wl->context.gpu, &queue_count, NULL);
if (queue_count < 1 || queue_count > 32)
goto error;
vkGetPhysicalDeviceQueueFamilyProperties(wl->context.gpu, &queue_count, queue_properties);
for (i = 0; i < queue_count; i++)
{
if (queue_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
{
wl->context.graphics_queue_index = i;
RARCH_LOG("[Wayland/Vulkan]: Device supports %u sub-queues.\n",
queue_properties[i].queueCount);
found_queue = true;
break;
}
}
if (!found_queue)
{
RARCH_ERR("[Wayland/Vulkan]: Did not find suitable graphics queue.\n");
goto error;
}
queue_info.queueFamilyIndex = wl->context.graphics_queue_index;
queue_info.queueCount = 1;
queue_info.pQueuePriorities = &one;
device_info.queueCreateInfoCount = 1;
device_info.pQueueCreateInfos = &queue_info;
device_info.enabledExtensionCount = ARRAY_SIZE(device_extensions);
device_info.ppEnabledExtensionNames = device_extensions;
device_info.pEnabledFeatures = &features;
if (cached_device)
{
wl->context.device = cached_device;
cached_device = NULL;
video_driver_ctl(RARCH_DISPLAY_CTL_SET_VIDEO_CACHE_CONTEXT_ACK, NULL);
RARCH_LOG("[Vulkan]: Using cached Vulkan context.\n");
}
else if (vkCreateDevice(wl->context.gpu, &device_info, NULL, &wl->context.device) != VK_SUCCESS)
goto error;
vkGetDeviceQueue(wl->context.device, wl->context.graphics_queue_index, 0, &wl->context.queue);
GET_INSTANCE_PROC_ADDR(wl->context.instance, GetPhysicalDeviceSurfaceSupportKHR);
GET_INSTANCE_PROC_ADDR(wl->context.instance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
GET_INSTANCE_PROC_ADDR(wl->context.instance, GetPhysicalDeviceSurfaceFormatsKHR);
GET_INSTANCE_PROC_ADDR(wl->context.instance, GetPhysicalDeviceSurfacePresentModesKHR);
GET_INSTANCE_PROC_ADDR(wl->context.instance, CreateWaylandSurfaceKHR);
GET_INSTANCE_PROC_ADDR(wl->context.instance, DestroySurfaceKHR);
GET_DEVICE_PROC_ADDR(wl->context.device, CreateSwapchainKHR);
GET_DEVICE_PROC_ADDR(wl->context.device, DestroySwapchainKHR);
GET_DEVICE_PROC_ADDR(wl->context.device, GetSwapchainImagesKHR);
GET_DEVICE_PROC_ADDR(wl->context.device, AcquireNextImageKHR);
GET_DEVICE_PROC_ADDR(wl->context.device, QueuePresentKHR);
wl->context.queue_lock = slock_new();
if (!wl->context.queue_lock)
goto error;
return wl;
error:
gfx_ctx_wl_destroy_resources(wl);
free(wl);
return NULL;
}
static void gfx_ctx_wl_destroy(void *data)
{
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
if (!wl)
return;
gfx_ctx_wl_destroy_resources(wl);
if (wl->context.queue_lock)
slock_free(wl->context.queue_lock);
free(wl);
}
static void vulkan_acquire_next_image(gfx_ctx_wayland_data_t *wl)
{
VkResult err;
VkSemaphoreCreateInfo sem_info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
VkFenceCreateInfo info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
VkFence fence;
VkFence *next_fence;
unsigned index;
vkCreateFence(wl->context.device, &info, NULL, &fence);
err = wl->fpAcquireNextImageKHR(wl->context.device, wl->swapchain, UINT64_MAX,
VK_NULL_HANDLE, fence, &wl->context.current_swapchain_index);
index = wl->context.current_swapchain_index;
if (wl->context.swapchain_semaphores[index] == VK_NULL_HANDLE)
vkCreateSemaphore(wl->context.device, &sem_info, NULL, &wl->context.swapchain_semaphores[index]);
vkWaitForFences(wl->context.device, 1, &fence, true, UINT64_MAX);
vkDestroyFence(wl->context.device, fence, NULL);
next_fence = &wl->context.swapchain_fences[index];
if (*next_fence != VK_NULL_HANDLE)
{
vkWaitForFences(wl->context.device, 1, next_fence, true, UINT64_MAX);
vkResetFences(wl->context.device, 1, next_fence);
}
else
vkCreateFence(wl->context.device, &info, NULL, next_fence);
if (err != VK_SUCCESS)
{
RARCH_LOG("[Wayland/Vulkan]: AcquireNextImage failed, invalidating swapchain.\n");
wl->context.invalid_swapchain = true;
}
}
static bool vulkan_create_swapchain(gfx_ctx_wayland_data_t *wl)
{
VkSurfaceCapabilitiesKHR surface_properties;
VkSurfaceFormatKHR formats[256];
VkSurfaceFormatKHR format;
VkExtent2D swapchain_size;
VkPresentModeKHR swapchain_present_mode = wl->swap_interval ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_MAILBOX_KHR;
VkSurfaceTransformFlagBitsKHR pre_transform;
VkSwapchainKHR old_swapchain;
VkSwapchainCreateInfoKHR info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
uint32_t format_count;
uint32_t desired_swapchain_images;
unsigned i;
RARCH_LOG("[Wayland/Vulkan]: Creating swapchain with present mode: %u\n", (unsigned)swapchain_present_mode);
wl->fpGetPhysicalDeviceSurfaceCapabilitiesKHR(wl->context.gpu, wl->surface, &surface_properties);
wl->fpGetPhysicalDeviceSurfaceFormatsKHR(wl->context.gpu, wl->surface, &format_count, NULL);
wl->fpGetPhysicalDeviceSurfaceFormatsKHR(wl->context.gpu, wl->surface, &format_count, formats);
if (format_count == 1 && formats[0].format == VK_FORMAT_UNDEFINED)
{
format = formats[0];
format.format = VK_FORMAT_B8G8R8A8_UNORM;
}
else
{
if (format_count == 0)
{
RARCH_ERR("[Wayland Vulkan]: Surface has no formats.\n");
return false;
}
format = formats[0];
}
if (surface_properties.currentExtent.width == -1)
{
swapchain_size.width = wl->width;
swapchain_size.height = wl->height;
}
else
swapchain_size = surface_properties.currentExtent;
desired_swapchain_images = surface_properties.minImageCount + 1;
if ((surface_properties.maxImageCount > 0) && (desired_swapchain_images > surface_properties.maxImageCount))
desired_swapchain_images = surface_properties.maxImageCount;
if (surface_properties.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
pre_transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
else
pre_transform = surface_properties.currentTransform;
old_swapchain = wl->swapchain;
info.surface = wl->surface;
info.minImageCount = desired_swapchain_images;
info.imageFormat = format.format;
info.imageColorSpace = format.colorSpace;
info.imageExtent.width = swapchain_size.width;
info.imageExtent.height = swapchain_size.height;
info.imageArrayLayers = 1;
info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
info.preTransform = pre_transform;
info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
info.presentMode = swapchain_present_mode;
info.clipped = true;
info.oldSwapchain = old_swapchain;
wl->fpCreateSwapchainKHR(wl->context.device, &info, NULL, &wl->swapchain);
if (old_swapchain != VK_NULL_HANDLE)
wl->fpDestroySwapchainKHR(wl->context.device, old_swapchain, NULL);
wl->context.swapchain_width = swapchain_size.width;
wl->context.swapchain_height = swapchain_size.height;
/* Make sure we create a backbuffer format that is as we expect. */
switch (format.format)
{
case VK_FORMAT_B8G8R8A8_SRGB:
wl->context.swapchain_format = VK_FORMAT_B8G8R8A8_UNORM;
wl->context.swapchain_is_srgb = true;
break;
case VK_FORMAT_R8G8B8A8_SRGB:
wl->context.swapchain_format = VK_FORMAT_R8G8B8A8_UNORM;
wl->context.swapchain_is_srgb = true;
break;
case VK_FORMAT_R8G8B8_SRGB:
wl->context.swapchain_format = VK_FORMAT_R8G8B8_UNORM;
wl->context.swapchain_is_srgb = true;
break;
case VK_FORMAT_B8G8R8_SRGB:
wl->context.swapchain_format = VK_FORMAT_B8G8R8_UNORM;
wl->context.swapchain_is_srgb = true;
break;
default:
wl->context.swapchain_format = format.format;
break;
}
wl->fpGetSwapchainImagesKHR(wl->context.device, wl->swapchain,
&wl->context.num_swapchain_images, NULL);
wl->fpGetSwapchainImagesKHR(wl->context.device, wl->swapchain,
&wl->context.num_swapchain_images, wl->context.swapchain_images);
for (i = 0; i < wl->context.num_swapchain_images; i++)
{
if (wl->context.swapchain_fences[i])
{
vkDestroyFence(wl->context.device, wl->context.swapchain_fences[i], NULL);
wl->context.swapchain_fences[i] = VK_NULL_HANDLE;
}
}
vulkan_acquire_next_image(wl);
return true;
}
static void vulkan_present(gfx_ctx_wayland_data_t *wl, unsigned index)
{
VkResult result = VK_SUCCESS, err = VK_SUCCESS;
VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
present.swapchainCount = 1;
present.pSwapchains = &wl->swapchain;
present.pImageIndices = &index;
present.pResults = &result;
present.waitSemaphoreCount = 1;
present.pWaitSemaphores = &wl->context.swapchain_semaphores[index];
/* Better hope QueuePresent doesn't block D: */
slock_lock(wl->context.queue_lock);
err = wl->fpQueuePresentKHR(wl->context.queue, &present);
if (err != VK_SUCCESS || result != VK_SUCCESS)
{
RARCH_LOG("[Wayland/Vulkan]: QueuePresent failed, invalidating swapchain.\n");
wl->context.invalid_swapchain = true;
}
slock_unlock(wl->context.queue_lock);
}
static void gfx_ctx_wl_swap_buffers(void *data)
{
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
vulkan_present(wl, wl->context.current_swapchain_index);
vulkan_acquire_next_image(wl);
flush_wayland_fd(wl);
}
static void gfx_ctx_wl_set_swap_interval(void *data, unsigned swap_interval)
{
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
if (wl->swap_interval != swap_interval)
{
wl->swap_interval = swap_interval;
if (wl->swapchain)
wl->need_new_swapchain = true;
}
}
static bool gfx_ctx_wl_set_video_mode(void *data,
unsigned width, unsigned height,
bool fullscreen)
{
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
VkWaylandSurfaceCreateInfoKHR wl_info = { VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR };
wl->width = width ? width : DEFAULT_WINDOWED_WIDTH;
wl->height = height ? height : DEFAULT_WINDOWED_HEIGHT;
/* TODO: Use wl_output::scale to obtain correct value. */
wl->buffer_scale = 1;
wl->surf = wl_compositor_create_surface(wl->compositor);
wl_surface_set_buffer_scale(wl->surf, wl->buffer_scale);
wl->shell_surf = wl_shell_get_shell_surface(wl->shell, wl->surf);
wl_shell_surface_add_listener(wl->shell_surf, &shell_surface_listener, wl);
wl_shell_surface_set_toplevel(wl->shell_surf);
wl_shell_surface_set_class(wl->shell_surf, "RetroArch");
wl_shell_surface_set_title(wl->shell_surf, "RetroArch");
if (fullscreen)
wl_shell_surface_set_fullscreen(wl->shell_surf, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, NULL);
flush_wayland_fd(wl);
wl_display_roundtrip(wl->dpy);
wl_info.display = wl->dpy;
wl_info.surface = wl->surf;
wl->fpCreateWaylandSurfaceKHR(wl->context.instance, &wl_info, NULL, &wl->surface);
if (!vulkan_create_swapchain(wl))
goto error;
return true;
error:
gfx_ctx_wl_destroy(data);
return false;
}
static void gfx_ctx_wl_input_driver(void *data,
const input_driver_t **input, void **input_data)
{
(void)data;
#if 0
void *wl = input_wayland.init();
*input = wl ? &input_wayland : NULL;
*input_data = wl;
#endif
*input = NULL;
*input_data = NULL;
}
static bool gfx_ctx_wl_has_focus(void *data)
{
(void)data;
return true;
}
static bool gfx_ctx_wl_suppress_screensaver(void *data, bool enable)
{
(void)data;
(void)enable;
return true;
}
static bool gfx_ctx_wl_has_windowed(void *data)
{
(void)data;
return true;
}
static bool gfx_ctx_wl_bind_api(void *video_driver,
enum gfx_ctx_api api, unsigned major, unsigned minor)
{
(void)video_driver;
(void)api;
(void)major;
(void)minor;
return api == GFX_CTX_VULKAN_API;
}
static void *gfx_ctx_wl_get_context_data(void *data)
{
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
return &wl->context;
}
const gfx_ctx_driver_t gfx_ctx_wayland_vulkan = {
gfx_ctx_wl_init,
gfx_ctx_wl_destroy,
gfx_ctx_wl_bind_api,
gfx_ctx_wl_set_swap_interval,
gfx_ctx_wl_set_video_mode,
gfx_ctx_wl_get_video_size,
NULL, /* get_video_output_size */
NULL, /* get_video_output_prev */
NULL, /* get_video_output_next */
NULL, /* get_metrics */
NULL,
gfx_ctx_wl_update_window_title,
gfx_ctx_wl_check_window,
gfx_ctx_wl_set_resize,
gfx_ctx_wl_has_focus,
gfx_ctx_wl_suppress_screensaver,
gfx_ctx_wl_has_windowed,
gfx_ctx_wl_swap_buffers,
gfx_ctx_wl_input_driver,
NULL,
NULL,
NULL,
NULL,
"wayland-vulkan",
NULL,
gfx_ctx_wl_get_context_data,
};

View File

@ -187,7 +187,7 @@ static void gl_raster_font_free_font(void *data)
if (font->font_driver && font->font_data) if (font->font_driver && font->font_data)
font->font_driver->free(font->font_data); font->font_driver->free(font->font_data);
video_driver_texture_unload((uintptr_t*)&font->tex); glDeleteTextures(1, &font->tex);
free(font); free(font);
} }
@ -197,8 +197,8 @@ static int gl_get_message_width(void *data, const char *msg,
gl_raster_t *font = (gl_raster_t*)data; gl_raster_t *font = (gl_raster_t*)data;
unsigned i; unsigned i;
unsigned msg_len = min(msg_len_full, MAX_MSG_LEN_CHUNK); unsigned msg_len = min(msg_len_full, MAX_MSG_LEN_CHUNK);
int delta_x = 0; int delta_x = 0;
if (!font) if (!font)
return 0; return 0;

View File

@ -0,0 +1,355 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2016 - Hans-Kristian Arntzen
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "../common/vulkan_common.h"
#include "../font_driver.h"
typedef struct
{
vk_t *vk;
struct vk_texture texture;
const font_renderer_driver_t *font_driver;
void *font_data;
struct vk_vertex *pv;
struct vk_buffer_range range;
unsigned vertices;
} vulkan_raster_t;
static void vulkan_raster_font_free_font(void *data);
static void *vulkan_raster_font_init_font(void *data,
const char *font_path, float font_size)
{
const struct font_atlas *atlas = NULL;
vulkan_raster_t *font = (vulkan_raster_t*)calloc(1, sizeof(*font));
VkComponentMapping swizzle = {
VK_COMPONENT_SWIZZLE_ONE,
VK_COMPONENT_SWIZZLE_ONE,
VK_COMPONENT_SWIZZLE_ONE,
VK_COMPONENT_SWIZZLE_R,
};
if (!font)
return NULL;
font->vk = (vk_t*)data;
if (!font_renderer_create_default((const void**)&font->font_driver,
&font->font_data, font_path, font_size))
{
RARCH_WARN("Couldn't initialize font renderer.\n");
free(font);
return NULL;
}
atlas = font->font_driver->get_atlas(font->font_data);
font->texture = vulkan_create_texture(font->vk, NULL,
atlas->width, atlas->height, VK_FORMAT_R8_UNORM, atlas->buffer, &swizzle, VULKAN_TEXTURE_STATIC);
return font;
}
static void vulkan_raster_font_free_font(void *data)
{
vulkan_raster_t *font = (vulkan_raster_t*)data;
if (!font)
return;
if (font->font_driver && font->font_data)
font->font_driver->free(font->font_data);
vkQueueWaitIdle(font->vk->context->queue);
vulkan_destroy_texture(font->vk->context->device, &font->texture);
free(font);
}
static int vulkan_get_message_width(void *data, const char *msg, unsigned msg_len, float scale)
{
vulkan_raster_t *font = (vulkan_raster_t*)data;
unsigned i;
int delta_x = 0;
if (!font)
return 0;
for (i = 0; i < msg_len; i++)
{
const struct font_glyph *glyph =
font->font_driver->get_glyph(font->font_data, (uint8_t)msg[i]);
if (!glyph) /* Do something smarter here ... */
glyph = font->font_driver->get_glyph(font->font_data, '?');
if (!glyph)
continue;
delta_x += glyph->advance_x;
}
return delta_x * scale;
}
static void vulkan_raster_font_render_line(
vulkan_raster_t *font, const char *msg, unsigned msg_len,
float scale, const float color[4], float pos_x,
float pos_y, unsigned text_align)
{
int x, y, delta_x, delta_y;
float inv_tex_size_x, inv_tex_size_y, inv_win_width, inv_win_height;
unsigned i;
struct vk_color vk_color;
vk_t *vk = font ? font->vk : NULL;
if (!vk)
return;
x = roundf(pos_x * vk->vp.width);
y = roundf((1.0f - pos_y) * vk->vp.height);
delta_x = 0;
delta_y = 0;
vk_color.r = color[0];
vk_color.g = color[1];
vk_color.b = color[2];
vk_color.a = color[3];
switch (text_align)
{
case TEXT_ALIGN_RIGHT:
x -= vulkan_get_message_width(font, msg, msg_len, scale);
break;
case TEXT_ALIGN_CENTER:
x -= vulkan_get_message_width(font, msg, msg_len, scale) / 2;
break;
}
inv_tex_size_x = 1.0f / font->texture.width;
inv_tex_size_y = 1.0f / font->texture.height;
inv_win_width = 1.0f / font->vk->vp.width;
inv_win_height = 1.0f / font->vk->vp.height;
for (i = 0; i < msg_len; i++)
{
int off_x, off_y, tex_x, tex_y, width, height;
const struct font_glyph *glyph =
font->font_driver->get_glyph(font->font_data, (uint8_t)msg[i]);
if (!glyph) /* Do something smarter here ... */
glyph = font->font_driver->get_glyph(font->font_data, '?');
if (!glyph)
continue;
off_x = glyph->draw_offset_x;
off_y = glyph->draw_offset_y;
tex_x = glyph->atlas_offset_x;
tex_y = glyph->atlas_offset_y;
width = glyph->width;
height = glyph->height;
vulkan_write_quad_vbo(font->pv + font->vertices,
(x + off_x + delta_x * scale) * inv_win_width, (y + off_y + delta_y * scale) * inv_win_height,
width * scale * inv_win_width, height * scale * inv_win_height,
tex_x * inv_tex_size_x, tex_y * inv_tex_size_y,
width * inv_tex_size_x, height * inv_tex_size_y,
&vk_color);
font->vertices += 6;
delta_x += glyph->advance_x;
delta_y += glyph->advance_y;
}
}
static void vulkan_raster_font_render_message(
vulkan_raster_t *font, const char *msg, float scale,
const float color[4], float pos_x, float pos_y,
unsigned text_align)
{
int lines = 0;
float line_height;
if (!msg || !*msg || !font->vk)
return;
/* If the font height is not supported just draw as usual */
if (!font->font_driver->get_line_height)
{
vulkan_raster_font_render_line(font, msg, strlen(msg), scale, color, pos_x, pos_y, text_align);
return;
}
line_height = scale / font->font_driver->get_line_height(font->font_data);
for (;;)
{
const char *delim = strchr(msg, '\n');
/* Draw the line */
if (delim)
{
unsigned msg_len = delim - msg;
vulkan_raster_font_render_line(font, msg, msg_len, scale, color, pos_x, pos_y - (float)lines * line_height, text_align);
msg += msg_len + 1;
lines++;
}
else
{
unsigned msg_len = strlen(msg);
vulkan_raster_font_render_line(font, msg, msg_len, scale, color, pos_x, pos_y - (float)lines * line_height, text_align);
break;
}
}
}
static void vulkan_raster_font_setup_viewport(vulkan_raster_t *font, bool full_screen)
{
unsigned width, height;
video_driver_get_size(&width, &height);
video_driver_set_viewport(width, height, full_screen, false);
}
static void vulkan_raster_font_flush(vulkan_raster_t *font)
{
const struct vk_draw_triangles call = {
font->vk->pipelines.alpha_blend,
&font->texture,
font->vk->samplers.nearest,
&font->vk->mvp,
&font->range,
font->vertices,
};
vulkan_draw_triangles(font->vk, &call);
}
static void vulkan_raster_font_render_msg(void *data, const char *msg,
const void *userdata)
{
float x, y, scale, drop_mod;
float color[4], color_dark[4];
int drop_x, drop_y;
bool full_screen;
enum text_alignment text_align;
vk_t *vk = NULL;
vulkan_raster_t *font = (vulkan_raster_t*)data;
settings_t *settings = config_get_ptr();
const struct font_params *params = (const struct font_params*)userdata;
unsigned max_glyphs;
if (!font || !msg || !*msg)
return;
vk = font->vk;
if (params)
{
x = params->x;
y = params->y;
scale = params->scale;
full_screen = params->full_screen;
text_align = params->text_align;
drop_x = params->drop_x;
drop_y = params->drop_y;
drop_mod = params->drop_mod;
color[0] = FONT_COLOR_GET_RED(params->color) / 255.0f;
color[1] = FONT_COLOR_GET_GREEN(params->color) / 255.0f;
color[2] = FONT_COLOR_GET_BLUE(params->color) / 255.0f;
color[3] = FONT_COLOR_GET_ALPHA(params->color) / 255.0f;
/* If alpha is 0.0f, turn it into default 1.0f */
if (color[3] <= 0.0f)
color[3] = 1.0f;
}
else
{
x = settings->video.msg_pos_x;
y = settings->video.msg_pos_y;
scale = 1.0f;
full_screen = true;
text_align = TEXT_ALIGN_LEFT;
color[0] = settings->video.msg_color_r;
color[1] = settings->video.msg_color_g;
color[2] = settings->video.msg_color_b;
color[3] = 1.0f;
drop_x = -2;
drop_y = -2;
drop_mod = 0.3f;
}
vulkan_raster_font_setup_viewport(font, full_screen);
max_glyphs = strlen(msg);
if (drop_x || drop_y)
max_glyphs *= 2;
if (!vulkan_buffer_chain_alloc(font->vk->context, &font->vk->chain->vbo,
6 * sizeof(struct vk_vertex) * max_glyphs, &font->range))
return;
font->vertices = 0;
font->pv = (struct vk_vertex*)font->range.data;
if (drop_x || drop_y)
{
color_dark[0] = color[0] * drop_mod;
color_dark[1] = color[1] * drop_mod;
color_dark[2] = color[2] * drop_mod;
color_dark[3] = color[3];
vulkan_raster_font_render_message(font, msg, scale, color_dark,
x + scale * drop_x / vk->vp.width, y +
scale * drop_y / vk->vp.height, text_align);
}
vulkan_raster_font_render_message(font, msg, scale, color, x, y, text_align);
vulkan_raster_font_flush(font);
}
static const struct font_glyph *vulkan_raster_font_get_glyph(
void *data, uint32_t code)
{
vulkan_raster_t *font = (vulkan_raster_t*)data;
if (!font || !font->font_driver)
return NULL;
if (!font->font_driver->ident)
return NULL;
return font->font_driver->get_glyph((void*)font->font_driver, code);
}
static void vulkan_raster_font_flush_block(void *data)
{
(void)data;
}
static void vulkan_raster_font_bind_block(void *data, void *userdata)
{
(void)data;
}
font_renderer_t vulkan_raster_font = {
vulkan_raster_font_init_font,
vulkan_raster_font_free_font,
vulkan_raster_font_render_msg,
"Vulkan raster",
vulkan_raster_font_get_glyph,
vulkan_raster_font_bind_block,
vulkan_raster_font_flush_block,
vulkan_get_message_width
};

View File

@ -0,0 +1,123 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2016 - Hans-Kristian Arntzen
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string>
#include <sstream>
#include "glslang_util.hpp"
#include "glslang.hpp"
#include "../../general.h"
#include "libretro-common/include/retro_file.h"
#include "libretro-common/include/string/string_list.h"
using namespace std;
bool read_file(const char *path, vector<string> *output)
{
char *buf = nullptr;
ssize_t len = 0;
if (retro_read_file(path, (void**)&buf, &len) < 0)
{
RARCH_ERR("Failed to open shader file: \"%s\".\n", path);
return false;
}
struct string_list *list = string_split(buf, "\n");
if (!list)
{
free(buf);
return false;
}
if (list->size == 0)
{
free(buf);
string_list_free(list);
return false;
}
output->clear();
for (size_t i = 0; i < list->size; i++)
output->push_back(list->elems[i].data);
string_list_free(list);
return true;
}
string build_stage_source(const vector<string> &lines, const char *stage)
{
ostringstream str;
// Version header.
str << lines.front();
str << '\n';
bool active = true;
for (auto itr = begin(lines) + 1; itr != end(lines); ++itr)
{
if (itr->find("#pragma stage ") != string::npos)
{
auto expected = string("#pragma stage ") + stage;
active = itr->find(expected) != string::npos;
// Improve debuggability.
if (active)
{
str << "#line ";
str << (itr - begin(lines)) + 2;
str << '\n';
}
}
else if (active)
str << *itr;
str << '\n';
}
return str.str();
}
bool glslang_compile_shader(const char *shader_path, glslang_output *output)
{
RARCH_LOG("Compiling shader \"%s\".\n", shader_path);
vector<string> lines;
if (!read_file(shader_path, &lines))
return false;
auto &header = lines.front();
if (header.find_first_of("#version ") == string::npos)
{
RARCH_ERR("First line of the shader must contain a valid #version string.\n");
return false;
}
if (!glslang::compile_spirv(build_stage_source(lines, "vertex"),
glslang::StageVertex, &output->vertex))
{
RARCH_ERR("Failed to compile vertex shader stage.\n");
return false;
}
if (!glslang::compile_spirv(build_stage_source(lines, "fragment"),
glslang::StageFragment, &output->fragment))
{
RARCH_ERR("Failed to compile fragment shader stage.\n");
return false;
}
return true;
}

View File

@ -0,0 +1,32 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2016 - Hans-Kristian Arntzen
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLSLANG_UTIL_HPP
#define GLSLANG_UTIL_HPP
#include <stdint.h>
#include <vector>
struct glslang_output
{
std::vector<uint32_t> vertex;
std::vector<uint32_t> fragment;
};
bool glslang_compile_shader(const char *shader_path, glslang_output *output);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,133 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2016 - Hans-Kristian Arntzen
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SHADER_VULKAN_H
#define SHADER_VULKAN_H
#include <stdint.h>
#include <stddef.h>
#include <vulkan/vulkan.h>
#include "libretro-common/include/boolean.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct vulkan_filter_chain vulkan_filter_chain_t;
enum vulkan_filter_chain_filter
{
VULKAN_FILTER_CHAIN_LINEAR = 0,
VULKAN_FILTER_CHAIN_NEAREST = 1
};
struct vulkan_filter_chain_texture
{
VkImageView view;
VkImageLayout layout;
unsigned width;
unsigned height;
};
enum vulkan_filter_chain_scale
{
VULKAN_FILTER_CHAIN_SCALE_ORIGINAL,
VULKAN_FILTER_CHAIN_SCALE_SOURCE,
VULKAN_FILTER_CHAIN_SCALE_VIEWPORT,
VULKAN_FILTER_CHAIN_SCALE_ABSOLUTE
};
struct vulkan_filter_chain_pass_info
{
/* For the last pass, make sure VIEWPORT scale with scale factors of 1 are used. */
enum vulkan_filter_chain_scale scale_type_x;
enum vulkan_filter_chain_scale scale_type_y;
float scale_x;
float scale_y;
/* Ignored for the last pass, swapchain info will be used instead. */
VkFormat rt_format;
/* The filter to use for source in this pass. */
enum vulkan_filter_chain_filter source_filter;
};
struct vulkan_filter_chain_swapchain_info
{
VkViewport viewport;
VkFormat format;
VkRenderPass render_pass;
unsigned num_indices;
};
struct vulkan_filter_chain_create_info
{
VkDevice device;
const VkPhysicalDeviceMemoryProperties *memory_properties;
VkPipelineCache pipeline_cache;
unsigned num_passes;
struct
{
unsigned width, height;
} max_input_size;
struct vulkan_filter_chain_swapchain_info swapchain;
};
vulkan_filter_chain_t *vulkan_filter_chain_new(
const struct vulkan_filter_chain_create_info *info);
void vulkan_filter_chain_free(vulkan_filter_chain_t *chain);
void vulkan_filter_chain_set_shader(vulkan_filter_chain_t *chain,
unsigned pass,
VkShaderStageFlags stage,
const uint32_t *spirv,
size_t spirv_words);
void vulkan_filter_chain_set_pass_info(vulkan_filter_chain_t *chain,
unsigned pass,
const struct vulkan_filter_chain_pass_info *info);
bool vulkan_filter_chain_update_swapchain_info(vulkan_filter_chain_t *chain,
const struct vulkan_filter_chain_swapchain_info *info);
void vulkan_filter_chain_notify_sync_index(vulkan_filter_chain_t *chain,
unsigned index);
bool vulkan_filter_chain_init(vulkan_filter_chain_t *chain);
void vulkan_filter_chain_set_input_texture(vulkan_filter_chain_t *chain,
const struct vulkan_filter_chain_texture *texture);
void vulkan_filter_chain_build_offscreen_passes(vulkan_filter_chain_t *chain,
VkCommandBuffer cmd, const VkViewport *vp);
void vulkan_filter_chain_build_viewport_pass(vulkan_filter_chain_t *chain,
VkCommandBuffer cmd, const VkViewport *vp, const float *mvp);
vulkan_filter_chain_t *vulkan_filter_chain_create_default(const struct vulkan_filter_chain_create_info *info,
enum vulkan_filter_chain_filter filter);
vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset(const struct vulkan_filter_chain_create_info *info,
const char *path, enum vulkan_filter_chain_filter filter);
struct video_shader *vulkan_filter_chain_get_preset(vulkan_filter_chain_t *chain);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -140,6 +140,34 @@ static bool gl_font_init_first(
} }
#endif #endif
#ifdef HAVE_VULKAN
static const font_renderer_t *vulkan_font_backends[] = {
&vulkan_raster_font,
NULL,
};
static bool vulkan_font_init_first(
const void **font_driver, void **font_handle,
void *video_data, const char *font_path, float font_size)
{
unsigned i;
for (i = 0; vulkan_font_backends[i]; i++)
{
void *data = vulkan_font_backends[i]->init(video_data, font_path, font_size);
if (!data)
continue;
*font_driver = vulkan_font_backends[i];
*font_handle = data;
return true;
}
return false;
}
#endif
#ifdef HAVE_VITA2D #ifdef HAVE_VITA2D
static const font_renderer_t *vita2d_font_backends[] = { static const font_renderer_t *vita2d_font_backends[] = {
&vita2d_vita_font &vita2d_vita_font
@ -188,6 +216,11 @@ static bool font_init_first(
return gl_font_init_first(font_driver, font_handle, return gl_font_init_first(font_driver, font_handle,
video_data, font_path, font_size); video_data, font_path, font_size);
#endif #endif
#ifdef HAVE_VULKAN
case FONT_DRIVER_RENDER_VULKAN_API:
return vulkan_font_init_first(font_driver, font_handle,
video_data, font_path, font_size);
#endif
#ifdef HAVE_VITA2D #ifdef HAVE_VITA2D
case FONT_DRIVER_RENDER_VITA2D: case FONT_DRIVER_RENDER_VITA2D:
return vita2d_font_init_first(font_driver, font_handle, return vita2d_font_init_first(font_driver, font_handle,

View File

@ -30,7 +30,8 @@ enum font_driver_render_api
FONT_DRIVER_RENDER_DONT_CARE, FONT_DRIVER_RENDER_DONT_CARE,
FONT_DRIVER_RENDER_OPENGL_API, FONT_DRIVER_RENDER_OPENGL_API,
FONT_DRIVER_RENDER_DIRECT3D_API, FONT_DRIVER_RENDER_DIRECT3D_API,
FONT_DRIVER_RENDER_VITA2D FONT_DRIVER_RENDER_VITA2D,
FONT_DRIVER_RENDER_VULKAN_API
}; };
enum text_alignment enum text_alignment
@ -149,6 +150,7 @@ extern font_renderer_t d3d_xbox360_font;
extern font_renderer_t d3d_xdk1_font; extern font_renderer_t d3d_xdk1_font;
extern font_renderer_t d3d_win32_font; extern font_renderer_t d3d_win32_font;
extern font_renderer_t vita2d_vita_font; extern font_renderer_t vita2d_vita_font;
extern font_renderer_t vulkan_raster_font;
extern font_renderer_driver_t stb_font_renderer; extern font_renderer_driver_t stb_font_renderer;
extern font_renderer_driver_t freetype_font_renderer; extern font_renderer_driver_t freetype_font_renderer;

View File

@ -52,6 +52,9 @@ static const gfx_ctx_driver_t *gfx_ctx_drivers[] = {
#if defined(HAVE_WAYLAND) && defined(HAVE_OPENGL) && defined(HAVE_EGL) #if defined(HAVE_WAYLAND) && defined(HAVE_OPENGL) && defined(HAVE_EGL)
&gfx_ctx_wayland, &gfx_ctx_wayland,
#endif #endif
#if defined(HAVE_WAYLAND) && defined(HAVE_VULKAN)
&gfx_ctx_wayland_vulkan,
#endif
#if defined(HAVE_X11) && defined(HAVE_OPENGL) && defined(HAVE_EGL) #if defined(HAVE_X11) && defined(HAVE_OPENGL) && defined(HAVE_EGL)
&gfx_ctx_x_egl, &gfx_ctx_x_egl,
#endif #endif
@ -433,6 +436,13 @@ bool gfx_ctx_ctl(enum gfx_ctx_ctl_state state, void *data)
current_video_context->get_video_size(video_context_data, &mode_info->width, &mode_info->height); current_video_context->get_video_size(video_context_data, &mode_info->width, &mode_info->height);
} }
break; break;
case GFX_CTL_GET_CONTEXT_DATA:
{
if (!current_video_context || !current_video_context->get_context_data)
return false;
*(void**)data = current_video_context->get_context_data(video_context_data);
}
break;
case GFX_CTL_SET_VIDEO_CONTEXT_DATA: case GFX_CTL_SET_VIDEO_CONTEXT_DATA:
video_context_data = data; video_context_data = data;
break; break;

View File

@ -40,7 +40,8 @@ enum gfx_ctx_api
GFX_CTX_OPENGL_ES_API, GFX_CTX_OPENGL_ES_API,
GFX_CTX_DIRECT3D8_API, GFX_CTX_DIRECT3D8_API,
GFX_CTX_DIRECT3D9_API, GFX_CTX_DIRECT3D9_API,
GFX_CTX_OPENVG_API GFX_CTX_OPENVG_API,
GFX_CTX_VULKAN_API
}; };
enum display_metric_types enum display_metric_types
@ -83,7 +84,8 @@ enum gfx_ctx_ctl_state
GFX_CTL_SET_VIDEO_MODE, GFX_CTL_SET_VIDEO_MODE,
GFX_CTL_SET_RESIZE, GFX_CTL_SET_RESIZE,
GFX_CTL_GET_VIDEO_SIZE, GFX_CTL_GET_VIDEO_SIZE,
GFX_CTL_SET_VIDEO_CONTEXT_DATA GFX_CTL_SET_VIDEO_CONTEXT_DATA,
GFX_CTL_GET_CONTEXT_DATA
}; };
typedef void (*gfx_ctx_proc_t)(void); typedef void (*gfx_ctx_proc_t)(void);
@ -182,6 +184,11 @@ typedef struct gfx_ctx_driver
/* Optional. Binds HW-render offscreen context. */ /* Optional. Binds HW-render offscreen context. */
void (*bind_hw_render)(void *data, bool enable); void (*bind_hw_render)(void *data, bool enable);
/* Optional. Gets base data for the context which is used by the driver.
* This is mostly relevant for graphics APIs such as Vulkan
* which do not have global context state. */
void *(*get_context_data)(void *data);
} gfx_ctx_driver_t; } gfx_ctx_driver_t;
typedef struct gfx_ctx_size typedef struct gfx_ctx_size
@ -243,6 +250,7 @@ typedef struct gfx_ctx_ident
extern const gfx_ctx_driver_t gfx_ctx_sdl_gl; extern const gfx_ctx_driver_t gfx_ctx_sdl_gl;
extern const gfx_ctx_driver_t gfx_ctx_x_egl; extern const gfx_ctx_driver_t gfx_ctx_x_egl;
extern const gfx_ctx_driver_t gfx_ctx_wayland; extern const gfx_ctx_driver_t gfx_ctx_wayland;
extern const gfx_ctx_driver_t gfx_ctx_wayland_vulkan;
extern const gfx_ctx_driver_t gfx_ctx_glx; extern const gfx_ctx_driver_t gfx_ctx_glx;
extern const gfx_ctx_driver_t gfx_ctx_d3d; extern const gfx_ctx_driver_t gfx_ctx_d3d;
extern const gfx_ctx_driver_t gfx_ctx_drm_egl; extern const gfx_ctx_driver_t gfx_ctx_drm_egl;

View File

@ -145,6 +145,9 @@ struct aspect_ratio_elem aspectratio_lut[ASPECT_RATIO_END] = {
}; };
static const video_driver_t *video_drivers[] = { static const video_driver_t *video_drivers[] = {
#ifdef HAVE_VULKAN
&video_vulkan,
#endif
#ifdef HAVE_OPENGL #ifdef HAVE_OPENGL
&video_gl, &video_gl,
#endif #endif
@ -239,18 +242,57 @@ const char* config_get_video_driver_options(void)
return char_list_new_special(STRING_LIST_VIDEO_DRIVERS, NULL); return char_list_new_special(STRING_LIST_VIDEO_DRIVERS, NULL);
} }
static bool hw_render_context_is_vulkan(enum retro_hw_context_type type)
{
return type == RETRO_HW_CONTEXT_VULKAN;
}
static bool hw_render_context_is_gl(enum retro_hw_context_type type)
{
switch (type)
{
case RETRO_HW_CONTEXT_OPENGL:
case RETRO_HW_CONTEXT_OPENGLES2:
case RETRO_HW_CONTEXT_OPENGL_CORE:
case RETRO_HW_CONTEXT_OPENGLES3:
case RETRO_HW_CONTEXT_OPENGLES_VERSION:
return true;
default:
return false;
}
}
static bool find_video_driver(void) static bool find_video_driver(void)
{ {
int i; int i;
driver_ctx_info_t drv; driver_ctx_info_t drv;
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
#if defined(HAVE_OPENGL) && defined(HAVE_FBO) #if (defined(HAVE_OPENGL) && defined(HAVE_FBO)) || defined(HAVE_VULKAN)
if (video_driver_ctl(RARCH_DISPLAY_CTL_IS_HW_CONTEXT, NULL)) if (video_driver_ctl(RARCH_DISPLAY_CTL_IS_HW_CONTEXT, NULL))
{ {
RARCH_LOG("Using HW render, OpenGL driver forced.\n"); struct retro_hw_render_callback *hwr =
current_video = &video_gl; video_driver_callback();
return true; current_video = NULL;
#if defined(HAVE_VULKAN)
if (hwr && hw_render_context_is_vulkan(hwr->context_type))
{
RARCH_LOG("Using HW render, Vulkan driver forced.\n");
current_video = &video_vulkan;
}
#endif
#if defined(HAVE_OPENGL) && defined(HAVE_FBO)
if (hwr && hw_render_context_is_gl(hwr->context_type))
{
RARCH_LOG("Using HW render, OpenGL driver forced.\n");
current_video = &video_gl;
}
#endif
if (current_video)
return true;
} }
#endif #endif
@ -557,11 +599,17 @@ static bool uninit_video_input(void)
static bool init_video_pixel_converter(unsigned size) static bool init_video_pixel_converter(unsigned size)
{ {
struct retro_hw_render_callback *hwr = video_driver_callback();
/* If pixel format is not 0RGB1555, we don't need to do /* If pixel format is not 0RGB1555, we don't need to do
* any internal pixel conversion. */ * any internal pixel conversion. */
if (video_driver_get_pixel_format() != RETRO_PIXEL_FORMAT_0RGB1555) if (video_driver_get_pixel_format() != RETRO_PIXEL_FORMAT_0RGB1555)
return true; return true;
/* No need to perform pixel conversion for HW rendering contexts. */
if (hwr && hwr->context_type != RETRO_HW_CONTEXT_NONE)
return true;
RARCH_WARN("0RGB1555 pixel format is deprecated, and will be slower. For 15/16-bit, RGB565 format is preferred.\n"); RARCH_WARN("0RGB1555 pixel format is deprecated, and will be slower. For 15/16-bit, RGB565 format is preferred.\n");
video_driver_scaler_ptr = (video_pixel_scaler_t*) video_driver_scaler_ptr = (video_pixel_scaler_t*)
@ -1722,6 +1770,13 @@ bool video_driver_ctl(enum rarch_display_ctl_state state, void *data)
return false; return false;
return video_driver_poke->get_current_software_framebuffer( return video_driver_poke->get_current_software_framebuffer(
video_driver_data, (struct retro_framebuffer *)data); video_driver_data, (struct retro_framebuffer *)data);
case RARCH_DISPLAY_CTL_GET_HW_RENDER_INTERFACE:
if (
!video_driver_poke ||
!video_driver_poke->get_hw_render_interface)
return false;
return video_driver_poke->get_hw_render_interface(video_driver_data,
(const struct retro_hw_render_interface**)data);
case RARCH_DISPLAY_CTL_VIEWPORT_INFO: case RARCH_DISPLAY_CTL_VIEWPORT_INFO:
if (!current_video || !current_video->viewport_info) if (!current_video || !current_video->viewport_info)
return false; return false;
@ -1978,7 +2033,7 @@ uintptr_t video_driver_window_get(void)
bool video_driver_texture_load(void *data, bool video_driver_texture_load(void *data,
enum texture_filter_type filter_type, enum texture_filter_type filter_type,
unsigned *id) uintptr_t *id)
{ {
#ifdef HAVE_THREADS #ifdef HAVE_THREADS
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
@ -2004,7 +2059,7 @@ bool video_driver_texture_unload(uintptr_t *id)
if (!video_driver_poke || !video_driver_poke->unload_texture) if (!video_driver_poke || !video_driver_poke->unload_texture)
return false; return false;
video_driver_poke->unload_texture(video_driver_data, id); video_driver_poke->unload_texture(video_driver_data, *id);
*id = 0;
return true; return true;
} }

View File

@ -157,6 +157,7 @@ enum rarch_display_ctl_state
RARCH_DISPLAY_CTL_GPU_RECORD_INIT, RARCH_DISPLAY_CTL_GPU_RECORD_INIT,
RARCH_DISPLAY_CTL_GPU_RECORD_DEINIT, RARCH_DISPLAY_CTL_GPU_RECORD_DEINIT,
RARCH_DISPLAY_CTL_GET_CURRENT_SOFTWARE_FRAMEBUFFER, RARCH_DISPLAY_CTL_GET_CURRENT_SOFTWARE_FRAMEBUFFER,
RARCH_DISPLAY_CTL_GET_HW_RENDER_INTERFACE,
RARCH_DISPLAY_CTL_VIEWPORT_INFO RARCH_DISPLAY_CTL_VIEWPORT_INFO
}; };
@ -197,9 +198,8 @@ typedef struct video_poke_interface
{ {
uintptr_t (*load_texture)(void *video_data, void *data, uintptr_t (*load_texture)(void *video_data, void *data,
bool threaded, enum texture_filter_type filter_type); bool threaded, enum texture_filter_type filter_type);
void (*unload_texture)(void *data, uintptr_t *id); void (*unload_texture)(void *data, uintptr_t id);
void (*set_video_mode)(void *data, unsigned width, void (*set_video_mode)(void *data, unsigned width, unsigned height, bool fullscreen);
unsigned height, bool fullscreen);
void (*set_filtering)(void *data, unsigned index, bool smooth); void (*set_filtering)(void *data, unsigned index, bool smooth);
void (*get_video_output_size)(void *data, void (*get_video_output_size)(void *data,
unsigned *width, unsigned *height); unsigned *width, unsigned *height);
@ -226,6 +226,8 @@ typedef struct video_poke_interface
struct video_shader *(*get_current_shader)(void *data); struct video_shader *(*get_current_shader)(void *data);
bool (*get_current_software_framebuffer)(void *data, bool (*get_current_software_framebuffer)(void *data,
struct retro_framebuffer *framebuffer); struct retro_framebuffer *framebuffer);
bool (*get_hw_render_interface)(void *data,
const struct retro_hw_render_interface **iface);
} video_poke_interface_t; } video_poke_interface_t;
typedef struct video_viewport typedef struct video_viewport
@ -534,7 +536,7 @@ void video_driver_window_set(uintptr_t idx);
bool video_driver_texture_load(void *data, bool video_driver_texture_load(void *data,
enum texture_filter_type filter_type, enum texture_filter_type filter_type,
unsigned *id); uintptr_t *id);
bool video_driver_texture_unload(uintptr_t *id); bool video_driver_texture_unload(uintptr_t *id);
@ -549,6 +551,7 @@ void gl_load_texture_data(uint32_t id,
#endif #endif
extern video_driver_t video_gl; extern video_driver_t video_gl;
extern video_driver_t video_vulkan;
extern video_driver_t video_psp1; extern video_driver_t video_psp1;
extern video_driver_t video_vita2d; extern video_driver_t video_vita2d;
extern video_driver_t video_ctr; extern video_driver_t video_ctr;

View File

@ -923,6 +923,8 @@ enum rarch_shader_type video_shader_parse_type(const char *path,
return RARCH_SHADER_CG; return RARCH_SHADER_CG;
else if (string_is_equal(ext, "glslp") || string_is_equal(ext, "glsl")) else if (string_is_equal(ext, "glslp") || string_is_equal(ext, "glsl"))
return RARCH_SHADER_GLSL; return RARCH_SHADER_GLSL;
else if (string_is_equal(ext, "slangp") || string_is_equal(ext, "slang"))
return RARCH_SHADER_SLANG;
return fallback; return fallback;
} }

View File

@ -47,7 +47,8 @@ enum rarch_shader_type
RARCH_SHADER_NONE = 0, RARCH_SHADER_NONE = 0,
RARCH_SHADER_CG, RARCH_SHADER_CG,
RARCH_SHADER_HLSL, RARCH_SHADER_HLSL,
RARCH_SHADER_GLSL RARCH_SHADER_GLSL,
RARCH_SHADER_SLANG
}; };
enum gfx_scale_type enum gfx_scale_type

View File

@ -1146,7 +1146,7 @@ static uintptr_t thread_load_texture(void *video_data, void *data,
return thr->poke->load_texture(thr->driver_data, data, threaded, filter_type); return thr->poke->load_texture(thr->driver_data, data, threaded, filter_type);
} }
static void thread_unload_texture(void *video_data, uintptr_t *id) static void thread_unload_texture(void *video_data, uintptr_t id)
{ {
thread_video_t *thr = (thread_video_t*)video_data; thread_video_t *thr = (thread_video_t*)video_data;

View File

@ -921,6 +921,31 @@ enum retro_mod
* writeable (and readable). * writeable (and readable).
*/ */
enum retro_hw_render_interface_type
{
RETRO_HW_RENDER_INTERFACE_VULKAN = 0,
RETRO_HW_RENDER_INTERFACE_DUMMY = INT_MAX
};
/* Base struct. All retro_hw_render_interface_* types
* contain at least these fields. */
struct retro_hw_render_interface
{
enum retro_hw_render_interface_type interface_type;
unsigned interface_version;
};
#define RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE (41 | RETRO_ENVIRONMENT_EXPERIMENTAL)
/* const struct retro_hw_render_interface ** --
* Returns an API specific rendering interface for accessing API specific data.
* Not all HW rendering APIs support or need this.
* The contents of the returned pointer is specific to the rendering API
* being used. See the various headers like libretro_vulkan.h, etc.
*
* GET_HW_RENDER_INTERFACE cannot be called before context_reset has been called.
* Similarly, after context_destroyed callback returns,
* the contents of the HW_RENDER_INTERFACE are invalidated.
*/
#define RETRO_MEMDESC_CONST (1 << 0) /* The frontend will never change this memory area once retro_load_game has returned. */ #define RETRO_MEMDESC_CONST (1 << 0) /* The frontend will never change this memory area once retro_load_game has returned. */
#define RETRO_MEMDESC_BIGENDIAN (1 << 1) /* The memory area contains big endian data. Default is little endian. */ #define RETRO_MEMDESC_BIGENDIAN (1 << 1) /* The memory area contains big endian data. Default is little endian. */
#define RETRO_MEMDESC_ALIGN_2 (1 << 16) /* All memory access in this area is aligned to their own size, or 2, whichever is smaller. */ #define RETRO_MEMDESC_ALIGN_2 (1 << 16) /* All memory access in this area is aligned to their own size, or 2, whichever is smaller. */
@ -1537,6 +1562,9 @@ enum retro_hw_context_type
* use the corresponding enums directly. */ * use the corresponding enums directly. */
RETRO_HW_CONTEXT_OPENGLES_VERSION = 5, RETRO_HW_CONTEXT_OPENGLES_VERSION = 5,
/* Vulkan, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE. */
RETRO_HW_CONTEXT_VULKAN = 6,
RETRO_HW_CONTEXT_DUMMY = INT_MAX RETRO_HW_CONTEXT_DUMMY = INT_MAX
}; };
@ -1559,23 +1587,28 @@ struct retro_hw_render_callback
*/ */
retro_hw_context_reset_t context_reset; retro_hw_context_reset_t context_reset;
/* Set by frontend. */ /* Set by frontend.
* TODO: This is rather obsolete. The frontend should not
* be providing preallocated framebuffers. */
retro_hw_get_current_framebuffer_t get_current_framebuffer; retro_hw_get_current_framebuffer_t get_current_framebuffer;
/* Set by frontend. */ /* Set by frontend. */
retro_hw_get_proc_address_t get_proc_address; retro_hw_get_proc_address_t get_proc_address;
/* Set if render buffers should have depth component attached. */ /* Set if render buffers should have depth component attached.
* TODO: Obsolete. */
bool depth; bool depth;
/* Set if stencil buffers should be attached. */ /* Set if stencil buffers should be attached.
* TODO: Obsolete. */
bool stencil; bool stencil;
/* If depth and stencil are true, a packed 24/8 buffer will be added. /* If depth and stencil are true, a packed 24/8 buffer will be added.
* Only attaching stencil is invalid and will be ignored. */ * Only attaching stencil is invalid and will be ignored. */
/* Use conventional bottom-left origin convention. If false, /* Use conventional bottom-left origin convention. If false,
* standard libretro top-left origin semantics are used. */ * standard libretro top-left origin semantics are used.
* TODO: Move to GL specific interface. */
bool bottom_left_origin; bool bottom_left_origin;
/* Major version number for core GL context or GLES 3.1+. */ /* Major version number for core GL context or GLES 3.1+. */
@ -1586,6 +1619,7 @@ struct retro_hw_render_callback
/* If this is true, the frontend will go very far to avoid /* If this is true, the frontend will go very far to avoid
* resetting context in scenarios like toggling fullscreen, etc. * resetting context in scenarios like toggling fullscreen, etc.
* TODO: Obsolete? Maybe frontend should just always assume this ...
*/ */
bool cache_context; bool cache_context;

210
libretro_vulkan.h Normal file
View File

@ -0,0 +1,210 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------------
* The following license statement only applies to this libretro API header (libretro_vulkan.h)
* ---------------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBRETRO_VULKAN_H__
#define LIBRETRO_VULKAN_H__
#include "libretro.h"
#include <vulkan/vulkan.h>
#define RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION 1
struct retro_vulkan_image
{
VkImageView image_view;
VkImageLayout image_layout;
VkImageViewCreateInfo create_info;
};
typedef void (*retro_vulkan_set_image_t)(void *handle,
const struct retro_vulkan_image *image,
uint32_t num_semaphores,
const VkSemaphore *semaphores);
typedef uint32_t (*retro_vulkan_get_sync_index_t)(void *handle);
typedef uint32_t (*retro_vulkan_get_sync_index_mask_t)(void *handle);
typedef void (*retro_vulkan_set_command_buffers_t)(void *handle,
uint32_t num_cmd,
const VkCommandBuffer *cmd);
typedef void (*retro_vulkan_wait_sync_index_t)(void *handle);
typedef void (*retro_vulkan_lock_queue_t)(void *handle);
typedef void (*retro_vulkan_unlock_queue_t)(void *handle);
/* Note on thread safety:
* The Vulkan API is heavily designed around multi-threading, and
* the libretro interface for it should also be threading friendly.
* A core should be able to build command buffers and submit command buffers to the GPU from
* any thread.
*/
struct retro_hw_render_interface_vulkan
{
/* Must be set to RETRO_HW_RENDER_INTERFACE_VULKAN. */
enum retro_hw_render_interface_type interface_type;
/* Must be set to RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION. */
unsigned interface_version;
/* Opaque handle to the Vulkan backend in the frontend
* which must be passed along to all function pointers
* in this interface.
*
* The rationale for including a handle here (which libretro v1 doesn't current do in general) is:
*
* - Vulkan cores should be able to be freely threaded without lots of fuzz.
* This would break frontends which currently rely on TLS
* to deal with multiple cores loaded at the same time.
* - Fixing this in general is TODO for an eventual libretro v2.
*/
void *handle;
/* The Vulkan instance the context is using. */
VkInstance instance;
/* The physical device used. */
VkPhysicalDevice gpu;
/* The logical device used. */
VkDevice device;
/* The queue the core must use to submit data.
* This queue and index must remain constant throughout the lifetime
* of the context.
*
* This queue will be the queue that supports graphics and compute
* if the device supports compute.
*/
VkQueue queue;
unsigned queue_index;
/* Before calling retro_video_refresh_t with RETRO_HW_FRAME_BUFFER_VALID,
* set which image to use for this frame.
*
* If num_semaphores is non-zero, the frontend will wait for the semaphores provided to be signaled
* before using the results further in the pipeline.
* Using semaphores is optional for synchronization purposes, but if not using
* semaphores, an image memory barrier in vkCmdPipelineBarrier should be used in the graphics_queue.
* Example:
*
* vkCmdPipelineBarrier(cmd,
* srcStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
* dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
* image_memory_barrier = {
* srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
* dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
* });
*
* The use of pipeline barriers instead of semaphores is encouraged as it is simpler
* and more fine-grained. A layout transition must generally happen anyways which requires a
* pipeline barrier.
*
* The image passed to set_image must have imageUsage flags set to at least
* VK_IMAGE_USAGE_TRANSFER_SRC_BIT and VK_IMAGE_USAGE_SAMPLED_BIT.
* The core will naturally want to use flags such as
* VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT and/or VK_IMAGE_USAGE_TRANSFER_DST_BIT depending
* on how the final image is created.
*
* The image must also have been created with MUTABLE_FORMAT bit set if 8-bit formatt are used, so that the frontend can
* reinterpret sRGB formats as it sees fit.
*
* Images passed to set_image should be created with TILING_OPTIMAL.
* The image layout should be transitioned to either VK_IMAGE_LAYOUT_GENERIC or VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL.
* The actual image layout used must be set in image_layout.
*
* The image must be a 2D texture which may or not be layered and/or mipmapped.
* The image must be suitable for linear sampling.
* While the image_view is typically the only field used,
* the frontend may want to reinterpret the texture as sRGB vs. no-sRGB for example
* so the VkImageViewCreateInfo used to create the image view must also be passed in.
* The data in the pointer to the image struct will not be copied
* as the pNext field in create_info cannot be reliably deep-copied.
* The image pointer passed to set_image must be valid until retro_video_refresh_t has returned.
*
* If frame duping is used when passing NULL to retro_video_refresh_t,
* the frontend is free to either use the latest image passed to set_image or reuse the older pointer
* passed to set_image the frame RETRO_HW_FRAME_BUFFER_VALID was last used.
* Essentially, the lifetime of the pointer passed to retro_video_refresh_t should be extended
* if frame duping is used so that the frontend can reuse the older pointer.
*
* If frame duping is used, the frontend will not wait for any semaphores.
*/
retro_vulkan_set_image_t set_image;
/* Get the current sync index for this frame which is obtained in frontend by calling
* e.g. vkAcquireNextImageKHR before calling retro_run().
* This index will correspond to which swapchain buffer is currently the active one.
* Knowing this index is very useful for maintaining safe async CPU and GPU operation without stalling.
*
* The common pattern for synchronization is to receive fences when submitting command buffers
* to Vulkan (vkQueueSubmit) and add this fence to a list of fences for frame number get_sync_index().
* Next time we receive the same get_sync_index(), we can wait for the fences from before,
* which will usually return immediately as the frontend will generally also avoid letting the GPU run ahead too much.
* After the fence has signaled, we know that the GPU has completed all GPU work related to work submitted in the frame
* we last saw get_sync_index(). This means we can safely reuse or free resources allocated in this frame.
*
* In theory, even if we wait for the fences correctly, it is not technically safe to write to the image
* we earlier passed to the frontend since we're not waiting for the frontend GPU jobs to complete.
* The frontend will guarantee that the appropriate pipeline barrier
* in graphics_queue has been used such that VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT cannot
* start until the frontend is done with the image.
*/
retro_vulkan_get_sync_index_t get_sync_index;
/* Returns a bitmask of how many swapchain images we currently have in the frontend.
* If bit #N is set in the return value, get_sync_index can return N.
* Knowing this value is useful for preallocating per-frame management structures ahead of time.
*
* While this value will typically remain constant throughout the applications lifecycle,
* it may for example change if the frontend suddently changes fullscreen state and/or latency.
* If this value ever changes, it is safe to assume that the device is completely idle and all synchronization
* objects can be deleted right away as desired.
*/
retro_vulkan_get_sync_index_mask_t get_sync_index_mask;
/* Instead of submitting the command buffer to the queue first, the core can pass along
* its command buffer to the frontend, and the frontend will submit the command buffer together
* with the frontends command buffers. This has the advantage that the overhead of vkQueueSubmit
* can be amortized into a single call. For this mode, semaphores in set_image will be ignored, so
* vkCmdPipelineBarrier must be used to synchronize the core and frontend.
* The command buffers in set_command_buffers are only executed once, even if frame duping is used.
* If frame duping is used, set_image should be used for the frames which should be duped instead.
*
* Command buffers passed to the frontend with set_command_buffers
* must not actually be submitted to the GPU until retro_video_refresh_t is called.
* The frontend must submit the command buffer before submitting any other command buffers
* provided by set_command_buffers. */
retro_vulkan_set_command_buffers_t set_command_buffers;
/* Waits on CPU for device activity for the current sync index to complete.
* This is useful since the core will not have a relevant fence to sync with
* when the frontend is submitting the command buffers. */
retro_vulkan_wait_sync_index_t wait_sync_index;
/* If the core submits command buffers itself to any of the queues provided in this interface,
* the core must lock and unlock the frontend from racing on the VkQueue.
* Queue submission can happen on any thread.
* Even if queue submission happens on the same thread as retro_run(), the lock/unlock
* functions must still be called.
*
* NOTE: Queue submissions are heavy-weight. */
retro_vulkan_lock_queue_t lock_queue;
retro_vulkan_unlock_queue_t unlock_queue;
};
#endif

View File

@ -172,7 +172,7 @@ static void mui_context_reset_textures(mui_handle_t *mui,
video_texture_image_load(&ti, path); video_texture_image_load(&ti, path);
video_driver_texture_load(&ti, video_driver_texture_load(&ti,
TEXTURE_FILTER_MIPMAP_LINEAR, (unsigned*)&mui->textures.list[i].id); TEXTURE_FILTER_MIPMAP_LINEAR, &mui->textures.list[i].id);
video_texture_image_free(&ti); video_texture_image_free(&ti);
} }
@ -1003,7 +1003,7 @@ static void mui_allocate_white_texture(mui_handle_t *mui)
ti.pixels = (uint32_t*)&white_data; ti.pixels = (uint32_t*)&white_data;
video_driver_texture_load(&ti, video_driver_texture_load(&ti,
TEXTURE_FILTER_NEAREST, (unsigned*)&mui->textures.white); TEXTURE_FILTER_NEAREST, &mui->textures.white);
} }
static void mui_font(void) static void mui_font(void)
@ -1123,8 +1123,8 @@ static void mui_context_bg_destroy(mui_handle_t *mui)
if (!mui) if (!mui)
return; return;
video_driver_texture_unload((uintptr_t*)&mui->textures.bg.id); video_driver_texture_unload(&mui->textures.bg.id);
video_driver_texture_unload((uintptr_t*)&mui->textures.white); video_driver_texture_unload(&mui->textures.white);
} }
static void mui_context_destroy(void *data) static void mui_context_destroy(void *data)
@ -1136,7 +1136,7 @@ static void mui_context_destroy(void *data)
return; return;
for (i = 0; i < MUI_TEXTURE_LAST; i++) for (i = 0; i < MUI_TEXTURE_LAST; i++)
video_driver_texture_unload((uintptr_t*)&mui->textures.list[i].id); video_driver_texture_unload(&mui->textures.list[i].id);
menu_display_ctl(MENU_DISPLAY_CTL_FONT_MAIN_DEINIT, NULL); menu_display_ctl(MENU_DISPLAY_CTL_FONT_MAIN_DEINIT, NULL);
@ -1155,7 +1155,7 @@ static bool mui_load_image(void *userdata, void *data,
case MENU_IMAGE_WALLPAPER: case MENU_IMAGE_WALLPAPER:
mui_context_bg_destroy(mui); mui_context_bg_destroy(mui);
video_driver_texture_load(data, video_driver_texture_load(data,
TEXTURE_FILTER_MIPMAP_LINEAR, (unsigned*)&mui->textures.bg.id); TEXTURE_FILTER_MIPMAP_LINEAR, &mui->textures.bg.id);
mui_allocate_white_texture(mui); mui_allocate_white_texture(mui);
break; break;
case MENU_IMAGE_BOXART: case MENU_IMAGE_BOXART:

View File

@ -1053,8 +1053,8 @@ static void xmb_context_destroy_horizontal_list(xmb_handle_t *xmb)
if (!path || !strstr(path, ".lpl")) if (!path || !strstr(path, ".lpl"))
continue; continue;
video_driver_texture_unload((uintptr_t*)&node->icon); video_driver_texture_unload(&node->icon);
video_driver_texture_unload((uintptr_t*)&node->content_icon); video_driver_texture_unload(&node->content_icon);
} }
} }
@ -1168,14 +1168,14 @@ static void xmb_context_reset_horizontal_list(
video_texture_image_load(&ti, texturepath); video_texture_image_load(&ti, texturepath);
video_driver_texture_load(&ti, video_driver_texture_load(&ti,
TEXTURE_FILTER_MIPMAP_LINEAR, (unsigned*)&node->icon); TEXTURE_FILTER_MIPMAP_LINEAR, &node->icon);
video_texture_image_free(&ti); video_texture_image_free(&ti);
video_texture_image_load(&ti, content_texturepath); video_texture_image_load(&ti, content_texturepath);
video_driver_texture_load(&ti, video_driver_texture_load(&ti,
TEXTURE_FILTER_MIPMAP_LINEAR, (unsigned*)&node->content_icon); TEXTURE_FILTER_MIPMAP_LINEAR, &node->content_icon);
video_texture_image_free(&ti); video_texture_image_free(&ti);
} }
@ -2167,7 +2167,7 @@ static void xmb_context_bg_destroy(xmb_handle_t *xmb)
{ {
if (!xmb) if (!xmb)
return; return;
video_driver_texture_unload((uintptr_t*)&xmb->textures.bg.id); video_driver_texture_unload(&xmb->textures.bg.id);
} }
static bool xmb_load_image(void *userdata, void *data, menu_image_type_t type) static bool xmb_load_image(void *userdata, void *data, menu_image_type_t type)
@ -2185,7 +2185,7 @@ static bool xmb_load_image(void *userdata, void *data, menu_image_type_t type)
xmb_context_bg_destroy(xmb); xmb_context_bg_destroy(xmb);
video_driver_texture_load(data, video_driver_texture_load(data,
TEXTURE_FILTER_MIPMAP_LINEAR, TEXTURE_FILTER_MIPMAP_LINEAR,
(unsigned*)&xmb->textures.bg.id); &xmb->textures.bg.id);
break; break;
case MENU_IMAGE_BOXART: case MENU_IMAGE_BOXART:
{ {
@ -2193,7 +2193,7 @@ static bool xmb_load_image(void *userdata, void *data, menu_image_type_t type)
xmb->boxart_height = xmb->boxart_width xmb->boxart_height = xmb->boxart_width
* (float)img->height / (float)img->width; * (float)img->height / (float)img->width;
video_driver_texture_load(data, video_driver_texture_load(data,
TEXTURE_FILTER_MIPMAP_LINEAR, (unsigned*)&xmb->boxart); TEXTURE_FILTER_MIPMAP_LINEAR, &xmb->boxart);
} }
break; break;
} }
@ -2333,7 +2333,7 @@ static void xmb_context_reset_textures(
video_driver_texture_load(&ti, video_driver_texture_load(&ti,
TEXTURE_FILTER_MIPMAP_LINEAR, TEXTURE_FILTER_MIPMAP_LINEAR,
(unsigned*)&xmb->textures.list[i].id); &xmb->textures.list[i].id);
video_texture_image_free(&ti); video_texture_image_free(&ti);
} }
@ -2649,7 +2649,7 @@ static void xmb_context_destroy(void *data)
return; return;
for (i = 0; i < XMB_TEXTURE_LAST; i++) for (i = 0; i < XMB_TEXTURE_LAST; i++)
video_driver_texture_unload((uintptr_t*)&xmb->textures.list[i].id); video_driver_texture_unload(&xmb->textures.list[i].id);
xmb_context_destroy_horizontal_list(xmb); xmb_context_destroy_horizontal_list(xmb);
xmb_context_bg_destroy(xmb); xmb_context_bg_destroy(xmb);

View File

@ -1145,8 +1145,8 @@ static void zarch_context_bg_destroy(void *data)
zui_t *zui = (zui_t*)data; zui_t *zui = (zui_t*)data;
if (!zui) if (!zui)
return; return;
video_driver_texture_unload((uintptr_t*)&zui->textures.bg.id); video_driver_texture_unload(&zui->textures.bg.id);
video_driver_texture_unload((uintptr_t*)&zui->textures.white); video_driver_texture_unload(&zui->textures.white);
} }
static void zarch_context_destroy(void *data) static void zarch_context_destroy(void *data)
@ -1171,7 +1171,7 @@ static bool zarch_load_image(void *userdata,
zarch_context_bg_destroy(zui); zarch_context_bg_destroy(zui);
video_driver_texture_load(data, video_driver_texture_load(data,
TEXTURE_FILTER_MIPMAP_LINEAR, TEXTURE_FILTER_MIPMAP_LINEAR,
(unsigned*)&zui->textures.bg.id); &zui->textures.bg.id);
break; break;
case MENU_IMAGE_BOXART: case MENU_IMAGE_BOXART:
break; break;
@ -1191,7 +1191,7 @@ static void zarch_allocate_white_texture(zui_t *zui)
video_driver_texture_load(&ti, video_driver_texture_load(&ti,
TEXTURE_FILTER_NEAREST, TEXTURE_FILTER_NEAREST,
(unsigned*)&zui->textures.white); &zui->textures.white);
} }
static void zarch_context_reset(void *data) static void zarch_context_reset(void *data)

View File

@ -0,0 +1,236 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2016 - Hans-Kristian Arntzen
* Copyright (C) 2011-2015 - Daniel De Matteis
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <retro_miscellaneous.h>
#include "../../config.def.h"
#include "../../gfx/font_driver.h"
#include "../../gfx/video_context_driver.h"
#include "../../gfx/common/vulkan_common.h"
#include "../menu_display.h"
static const float vk_vertexes[] = {
0, 0,
0, 1,
1, 0,
1, 1
};
static const float vk_tex_coords[] = {
0, 0,
0, 1,
1, 0,
1, 1
};
static vk_t *vk_get_ptr(void)
{
vk_t *vk = (vk_t*)video_driver_get_ptr(false);
if (!vk)
return NULL;
return vk;
}
static void *menu_display_vk_get_default_mvp(void)
{
vk_t *vk = vk_get_ptr();
if (!vk)
return NULL;
return &vk->mvp_no_rot;
}
static unsigned to_display_pipeline(enum menu_display_prim_type prim_type, bool blend)
{
return ((prim_type == MENU_DISPLAY_PRIM_TRIANGLESTRIP) << 1) | (blend << 0);
}
static void menu_display_vk_draw(void *data)
/*
float x, float y,
unsigned width, unsigned height,
struct gfx_coords *coords,
void *matrix_data,
uintptr_t handle,
enum menu_display_prim_type prim_type) */
{
menu_display_ctx_draw_t *draw = (menu_display_ctx_draw_t*)data;
struct vk_texture *texture;
const float *vertex, *tex_coord, *color;
math_matrix_4x4 *mat;
struct vk_buffer_range range;
struct vk_vertex *pv;
unsigned i;
vk_t *vk = vk_get_ptr();
if (!vk)
return;
texture = (struct vk_texture*)draw->texture;
mat = (math_matrix_4x4*)draw->matrix_data;
vertex = draw->coords->vertex;
tex_coord = draw->coords->tex_coord;
color = draw->coords->color;
/* TODO - edge case */
if (draw->height <= 0)
draw->height = 1;
if (!mat)
mat = (math_matrix_4x4*)menu_display_vk_get_default_mvp();
if (!vertex)
vertex = &vk_vertexes[0];
if (!tex_coord)
tex_coord = &vk_tex_coords[0];
if (!texture)
texture = &vk->display.blank_texture;
vk->vk_vp.x = draw->x;
vk->vk_vp.y = vk->context->swapchain_height - draw->y - draw->height;
vk->vk_vp.width = draw->width;
vk->vk_vp.height = draw->height;
vk->vk_vp.minDepth = 0.0f;
vk->vk_vp.maxDepth = 1.0f;
vk->tracker.dirty |= VULKAN_DIRTY_DYNAMIC_BIT;
/* Bake interleaved VBO. Kinda ugly, we should probably try to move to
* an interleaved model to begin with ... */
if (!vulkan_buffer_chain_alloc(vk->context, &vk->chain->vbo,
draw->coords->vertices * sizeof(struct vk_vertex), &range))
return;
pv = (struct vk_vertex*)range.data;
for (i = 0; i < draw->coords->vertices; i++, pv++)
{
pv->x = *vertex++;
pv->y = *vertex++;
pv->tex_x = *tex_coord++;
pv->tex_y = *tex_coord++;
pv->color.r = *color++;
pv->color.g = *color++;
pv->color.b = *color++;
pv->color.a = *color++;
}
{
const struct vk_draw_triangles call = {
vk->display.pipelines[to_display_pipeline(draw->prim_type, vk->display.blend)],
texture,
texture->default_smooth ? vk->samplers.linear : vk->samplers.nearest,
mat,
&range,
draw->coords->vertices,
};
vulkan_draw_triangles(vk, &call);
}
}
static void menu_display_vk_draw_bg(void *data)
{
struct gfx_coords coords;
const float *new_vertex = NULL;
const float *new_tex_coord = NULL;
menu_display_ctx_draw_t *draw = (menu_display_ctx_draw_t*)data;
global_t *global = global_get_ptr();
settings_t *settings = config_get_ptr();
vk_t *vk = vk_get_ptr();
if (!vk || !draw)
return;
if (!new_vertex)
new_vertex = &vk_vertexes[0];
if (!new_tex_coord)
new_tex_coord = &vk_tex_coords[0];
coords.vertices = draw->vertex_count;
coords.vertex = new_vertex;
coords.tex_coord = new_tex_coord;
coords.color = (const float*)draw->color;
vk->display.blend = true;
menu_display_ctl(MENU_DISPLAY_CTL_SET_VIEWPORT, NULL);
if (
(settings->menu.pause_libretro
|| !rarch_ctl(RARCH_CTL_IS_INITED, NULL)
|| rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)
)
&& !draw->force_transparency && draw->texture)
coords.color = (const float*)draw->color2;
draw->x = 0;
draw->y = 0;
draw->coords = &coords;
draw->matrix_data = (math_matrix_4x4*)
menu_display_vk_get_default_mvp();
menu_display_vk_draw(draw);
vk->display.blend = false;
}
static void menu_display_vk_restore_clear_color(void)
{
//glClearColor(0.0f, 0.0f, 0.0f, 0.00f);
}
static void menu_display_vk_clear_color(void *data)
{
(void)data;
/* FIXME: This makes little sense in Vulkan.
* We shouldn't be clearing mid-screen nilly willy. */
}
static const float *menu_display_vk_get_tex_coords(void)
{
return &vk_tex_coords[0];
}
static void menu_display_vk_blend_begin(void)
{
vk_t *vk = vk_get_ptr();
vk->display.blend = true;
}
static void menu_display_vk_blend_end(void)
{
vk_t *vk = vk_get_ptr();
vk->display.blend = false;
}
static bool menu_display_vk_font_init_first(
void **font_handle, void *video_data, const char *font_path,
float font_size)
{
return font_driver_init_first(NULL, font_handle, video_data,
font_path, font_size, true, FONT_DRIVER_RENDER_VULKAN_API);
}
menu_display_ctx_driver_t menu_display_ctx_vulkan = {
menu_display_vk_draw,
menu_display_vk_draw_bg,
menu_display_vk_blend_begin,
menu_display_vk_blend_end,
menu_display_vk_restore_clear_color,
menu_display_vk_clear_color,
menu_display_vk_get_default_mvp,
menu_display_vk_get_tex_coords,
menu_display_vk_font_init_first,
MENU_VIDEO_DRIVER_VULKAN,
"menu_display_vulkan",
};

View File

@ -42,6 +42,9 @@ static menu_display_ctx_driver_t *menu_display_ctx_drivers[] = {
#endif #endif
#ifdef HAVE_OPENGL #ifdef HAVE_OPENGL
&menu_display_ctx_gl, &menu_display_ctx_gl,
#endif
#ifdef HAVE_VULKAN
&menu_display_ctx_vulkan,
#endif #endif
&menu_display_ctx_null, &menu_display_ctx_null,
NULL, NULL,
@ -72,6 +75,10 @@ static bool menu_display_check_compatibility(
if (string_is_equal(video_driver, "gl")) if (string_is_equal(video_driver, "gl"))
return true; return true;
break; break;
case MENU_VIDEO_DRIVER_VULKAN:
if (!strcmp(video_driver, "vulkan"))
return true;
break;
case MENU_VIDEO_DRIVER_DIRECT3D: case MENU_VIDEO_DRIVER_DIRECT3D:
if (string_is_equal(video_driver, "d3d")) if (string_is_equal(video_driver, "d3d"))
return true; return true;

View File

@ -93,6 +93,7 @@ enum menu_display_driver_type
{ {
MENU_VIDEO_DRIVER_GENERIC = 0, MENU_VIDEO_DRIVER_GENERIC = 0,
MENU_VIDEO_DRIVER_OPENGL, MENU_VIDEO_DRIVER_OPENGL,
MENU_VIDEO_DRIVER_VULKAN,
MENU_VIDEO_DRIVER_DIRECT3D MENU_VIDEO_DRIVER_DIRECT3D
}; };
@ -175,6 +176,7 @@ void menu_display_handle_wallpaper_upload(void *task_data,
void *user_data, const char *err); void *user_data, const char *err);
extern menu_display_ctx_driver_t menu_display_ctx_gl; extern menu_display_ctx_driver_t menu_display_ctx_gl;
extern menu_display_ctx_driver_t menu_display_ctx_vulkan;
extern menu_display_ctx_driver_t menu_display_ctx_d3d; extern menu_display_ctx_driver_t menu_display_ctx_d3d;
extern menu_display_ctx_driver_t menu_display_ctx_null; extern menu_display_ctx_driver_t menu_display_ctx_null;

View File

@ -3337,11 +3337,11 @@ int menu_displaylist_push_list(menu_displaylist_info_t *info, unsigned type)
break; break;
case DISPLAYLIST_SHADER_PRESET: case DISPLAYLIST_SHADER_PRESET:
info->type_default = MENU_FILE_SHADER_PRESET; info->type_default = MENU_FILE_SHADER_PRESET;
strlcpy(info->exts, "cgp|glslp", sizeof(info->exts)); strlcpy(info->exts, "cgp|glslp|slangp", sizeof(info->exts));
break; break;
case DISPLAYLIST_SHADER_PASS: case DISPLAYLIST_SHADER_PASS:
info->type_default = MENU_FILE_SHADER; info->type_default = MENU_FILE_SHADER;
strlcpy(info->exts, "cg|glsl", sizeof(info->exts)); strlcpy(info->exts, "cg|glsl|slang", sizeof(info->exts));
break; break;
case DISPLAYLIST_VIDEO_FILTERS: case DISPLAYLIST_VIDEO_FILTERS:
info->type_default = MENU_FILE_VIDEOFILTER; info->type_default = MENU_FILE_VIDEOFILTER;

View File

@ -299,6 +299,7 @@ typedef struct
/* Menu shader */ /* Menu shader */
char default_glslp[PATH_MAX_LENGTH]; char default_glslp[PATH_MAX_LENGTH];
char default_cgp[PATH_MAX_LENGTH]; char default_cgp[PATH_MAX_LENGTH];
char default_slangp[PATH_MAX_LENGTH];
char db_playlist_file[PATH_MAX_LENGTH]; char db_playlist_file[PATH_MAX_LENGTH];
} menu_handle_t; } menu_handle_t;

View File

@ -645,6 +645,9 @@ extern "C" {
#define MENU_VALUE_CGP 0x0b8865bfU #define MENU_VALUE_CGP 0x0b8865bfU
#define MENU_VALUE_GLSL 0x7c976537U #define MENU_VALUE_GLSL 0x7c976537U
#define MENU_VALUE_CG 0x0059776fU #define MENU_VALUE_CG 0x0059776fU
#define MENU_VALUE_SLANG 0x105ce63aU
#define MENU_VALUE_SLANGP 0x1bf9adeaU
#define MENU_VALUE_RETROPAD 0x9e6703e6U #define MENU_VALUE_RETROPAD 0x9e6703e6U
#define MENU_VALUE_RETROKEYBOARD 0x9d8b6ea2U #define MENU_VALUE_RETROKEYBOARD 0x9d8b6ea2U

View File

@ -63,10 +63,16 @@ void menu_shader_manager_init(menu_handle_t *menu)
sizeof(menu->default_glslp)); sizeof(menu->default_glslp));
path_remove_extension(menu->default_glslp); path_remove_extension(menu->default_glslp);
strlcat(menu->default_glslp, ".glslp", sizeof(menu->default_glslp)); strlcat(menu->default_glslp, ".glslp", sizeof(menu->default_glslp));
fill_pathname_base(menu->default_cgp, config_path, fill_pathname_base(menu->default_cgp, config_path,
sizeof(menu->default_cgp)); sizeof(menu->default_cgp));
path_remove_extension(menu->default_cgp); path_remove_extension(menu->default_cgp);
strlcat(menu->default_cgp, ".cgp", sizeof(menu->default_cgp)); strlcat(menu->default_cgp, ".cgp", sizeof(menu->default_cgp));
fill_pathname_base(menu->default_slangp, config_path,
sizeof(menu->default_slangp));
path_remove_extension(menu->default_slangp);
strlcat(menu->default_slangp, ".slangp", sizeof(menu->default_slangp));
} }
else else
{ {
@ -74,6 +80,8 @@ void menu_shader_manager_init(menu_handle_t *menu)
sizeof(menu->default_glslp)); sizeof(menu->default_glslp));
strlcpy(menu->default_cgp, "menu.cgp", strlcpy(menu->default_cgp, "menu.cgp",
sizeof(menu->default_cgp)); sizeof(menu->default_cgp));
strlcpy(menu->default_slangp, "menu.slangp",
sizeof(menu->default_slangp));
} }
ext = path_get_extension(settings->video.shader_path); ext = path_get_extension(settings->video.shader_path);
@ -83,6 +91,7 @@ void menu_shader_manager_init(menu_handle_t *menu)
{ {
case MENU_VALUE_GLSLP: case MENU_VALUE_GLSLP:
case MENU_VALUE_CGP: case MENU_VALUE_CGP:
case MENU_VALUE_SLANGP:
conf = config_file_new(settings->video.shader_path); conf = config_file_new(settings->video.shader_path);
if (conf) if (conf)
{ {
@ -97,6 +106,7 @@ void menu_shader_manager_init(menu_handle_t *menu)
break; break;
case MENU_VALUE_GLSL: case MENU_VALUE_GLSL:
case MENU_VALUE_CG: case MENU_VALUE_CG:
case MENU_VALUE_SLANG:
strlcpy(shader->pass[0].source.path, settings->video.shader_path, strlcpy(shader->pass[0].source.path, settings->video.shader_path,
sizeof(shader->pass[0].source.path)); sizeof(shader->pass[0].source.path));
shader->passes = 1; shader->passes = 1;
@ -118,6 +128,12 @@ void menu_shader_manager_init(menu_handle_t *menu)
conf = config_file_new(preset_path); conf = config_file_new(preset_path);
} }
if (!conf)
{
fill_pathname_join(preset_path, shader_dir, "menu.slangp", sizeof(preset_path));
conf = config_file_new(preset_path);
}
if (conf) if (conf)
{ {
if (video_shader_read_conf_cgp(conf, shader)) if (video_shader_read_conf_cgp(conf, shader))
@ -235,13 +251,17 @@ void menu_shader_manager_save_preset(
/* Append extension automatically as appropriate. */ /* Append extension automatically as appropriate. */
if ( !strstr(basename, ".cgp") if ( !strstr(basename, ".cgp")
&& !strstr(basename, ".glslp")) && !strstr(basename, ".glslp")
&& !strstr(basename, ".slangp"))
{ {
switch (type) switch (type)
{ {
case RARCH_SHADER_GLSL: case RARCH_SHADER_GLSL:
strlcat(buffer, ".glslp", sizeof(buffer)); strlcat(buffer, ".glslp", sizeof(buffer));
break; break;
case RARCH_SHADER_SLANG:
strlcat(buffer, ".slangp", sizeof(buffer));
break;
case RARCH_SHADER_CG: case RARCH_SHADER_CG:
strlcat(buffer, ".cgp", sizeof(buffer)); strlcat(buffer, ".cgp", sizeof(buffer));
break; break;
@ -250,8 +270,22 @@ void menu_shader_manager_save_preset(
} }
else else
{ {
const char *conf_path = (type == RARCH_SHADER_GLSL) ? const char *conf_path = NULL;
menu->default_glslp : menu->default_cgp; switch (type)
{
case RARCH_SHADER_GLSL:
conf_path = menu->default_glslp;
break;
case RARCH_SHADER_SLANG:
conf_path = menu->default_slangp;
break;
default:
case RARCH_SHADER_CG:
conf_path = menu->default_cgp;
break;
}
strlcpy(buffer, conf_path, sizeof(buffer)); strlcpy(buffer, conf_path, sizeof(buffer));
} }
@ -323,6 +357,7 @@ unsigned menu_shader_manager_get_type(const struct video_shader *shader)
{ {
case RARCH_SHADER_CG: case RARCH_SHADER_CG:
case RARCH_SHADER_GLSL: case RARCH_SHADER_GLSL:
case RARCH_SHADER_SLANG:
if (type == RARCH_SHADER_NONE) if (type == RARCH_SHADER_NONE)
type = pass_type; type = pass_type;
else if (type != pass_type) else if (type != pass_type)
@ -373,6 +408,8 @@ void menu_shader_manager_apply_changes(void)
shader_type = RARCH_SHADER_GLSL; shader_type = RARCH_SHADER_GLSL;
#elif defined(HAVE_CG) || defined(HAVE_HLSL) #elif defined(HAVE_CG) || defined(HAVE_HLSL)
shader_type = RARCH_SHADER_CG; shader_type = RARCH_SHADER_CG;
#elif defined(HAVE_VULKAN)
shader_type = RARCH_SHADER_SLANG;
#endif #endif
} }
menu_shader_manager_set_preset(NULL, shader_type, NULL); menu_shader_manager_set_preset(NULL, shader_type, NULL);

View File

@ -389,6 +389,7 @@ if [ "$OS" != 'Win32' ]; then
fi fi
check_lib STRCASESTR "$CLIB" strcasestr check_lib STRCASESTR "$CLIB" strcasestr
check_lib MMAP "$CLIB" mmap check_lib MMAP "$CLIB" mmap
check_lib VULKAN -lvulkan vkCreateInstance
check_pkgconf PYTHON python3 check_pkgconf PYTHON python3

View File

@ -70,3 +70,4 @@ HAVE_MMAP=auto # MMAP support
HAVE_QT=no # QT companion support HAVE_QT=no # QT companion support
HAVE_XSHM=no # XShm video driver support (disabled because it's just a dummied out stub) HAVE_XSHM=no # XShm video driver support (disabled because it's just a dummied out stub)
HAVE_CHEEVOS=yes # Disable Retro Achievements HAVE_CHEEVOS=yes # Disable Retro Achievements
HAVE_VULKAN=auto # Enable Vulkan support

View File

@ -627,6 +627,7 @@ static bool ffmpeg_init_muxer_post(ffmpeg_t *handle)
handle->video.encoder); handle->video.encoder);
stream->codec = handle->video.codec; stream->codec = handle->video.codec;
stream->time_base = stream->codec->time_base;
handle->muxer.vstream = stream; handle->muxer.vstream = stream;
handle->muxer.vstream->sample_aspect_ratio = handle->muxer.vstream->sample_aspect_ratio =
handle->video.codec->sample_aspect_ratio; handle->video.codec->sample_aspect_ratio;
@ -636,6 +637,7 @@ static bool ffmpeg_init_muxer_post(ffmpeg_t *handle)
stream = avformat_new_stream(handle->muxer.ctx, stream = avformat_new_stream(handle->muxer.ctx,
handle->audio.encoder); handle->audio.encoder);
stream->codec = handle->audio.codec; stream->codec = handle->audio.codec;
stream->time_base = stream->codec->time_base;
handle->muxer.astream = stream; handle->muxer.astream = stream;
} }

View File

@ -77,6 +77,8 @@
#define SHADER_EXT_GLSLP 0x0f840c87U #define SHADER_EXT_GLSLP 0x0f840c87U
#define SHADER_EXT_CG 0x0059776fU #define SHADER_EXT_CG 0x0059776fU
#define SHADER_EXT_CGP 0x0b8865bfU #define SHADER_EXT_CGP 0x0b8865bfU
#define SHADER_EXT_SLANG 0x105ce63aU
#define SHADER_EXT_SLANGP 0x1bf9adeaU
#define runloop_cmd_triggered(cmd, id) BIT64_GET(cmd->state[2], id) #define runloop_cmd_triggered(cmd, id) BIT64_GET(cmd->state[2], id)
@ -351,6 +353,10 @@ static void check_shader_dir(rarch_dir_list_t *dir_list,
case SHADER_EXT_GLSLP: case SHADER_EXT_GLSLP:
type = RARCH_SHADER_GLSL; type = RARCH_SHADER_GLSL;
break; break;
case SHADER_EXT_SLANG:
case SHADER_EXT_SLANGP:
type = RARCH_SHADER_SLANG;
break;
case SHADER_EXT_CG: case SHADER_EXT_CG:
case SHADER_EXT_CGP: case SHADER_EXT_CGP:
type = RARCH_SHADER_CG; type = RARCH_SHADER_CG;