summaryrefslogtreecommitdiff
path: root/video.cpp
diff options
context:
space:
mode:
authorquou <quou@disroot.org>2024-12-23 21:30:13 +1100
committerquou <quou@disroot.org>2024-12-23 21:30:13 +1100
commit078b97e48b5ad5fcf3f5a16bb081ea0efb1f931b (patch)
treed04bfb2d25952b214a4df303823ff7c5d2743681 /video.cpp
parentb293168cc158d65f1a5146f155921ff82119d1bc (diff)
send textures to shaders
Diffstat (limited to 'video.cpp')
-rw-r--r--video.cpp351
1 files changed, 343 insertions, 8 deletions
diff --git a/video.cpp b/video.cpp
index 8bc5080..d6180bd 100644
--- a/video.cpp
+++ b/video.cpp
@@ -8,6 +8,7 @@
#define max_rpos 64
#define max_pipelines 64
#define max_shaders 32
+#define max_samplers 16
extern "C" {
#include "memory.h"
@@ -27,7 +28,8 @@ extern "C" {
#include "glad_vk.h"
const char* device_exts[] = {
- VK_KHR_SWAPCHAIN_EXTENSION_NAME
+ VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+ VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME
};
extern "C" {
@@ -388,10 +390,18 @@ struct Shader_Vk : public Shader, public Late_Terminated {
int find_attribute(const char* name);
};
+ struct Desc {
+ char name[24];
+ int slot;
+ int stage;
+ };
+
SProgram_Type type;
VkShaderModule modules[shader_type_count];
char entrypoints[shader_type_count][24];
Vertex_Format vfd;
+ Desc* descs;
+ int desc_count;
bool init(Device_Vk* dev, Pack_File* f);
bool init_module(
@@ -406,6 +416,8 @@ struct Shader_Vk : public Shader, public Late_Terminated {
);
void destroy(Device_Vk* dev) override;
+ int find_descriptor(const char* name);
+
static VkShaderStageFlagBits stage(Shader_Type type) {
switch (type) {
case shader_type_vertex:
@@ -519,6 +531,9 @@ struct Pso_Key {
struct Pipeline_Vk {
VkPipeline pip;
VkPipelineLayout lay;
+ VkDescriptorPool dp;
+ VkDescriptorSet dset;
+ VkDescriptorSetLayout dlay;
int age;
void init(Device_Vk* dev, const Pso_Key& desc);
@@ -576,6 +591,11 @@ struct Pipeline_Vk {
Device_Vk* dev,
const Pipeline& desc
);
+ void init_descriptors(
+ Device_Vk* dev,
+ const Pipeline& desc,
+ int count
+ );
void on_submit() {
age = 0;
@@ -594,6 +614,17 @@ struct Vertex_Format_Vk {
static VkFormat format_from_svar_type(SVariable_Type type);
};
+struct Sampler_Vk : public Late_Terminated {
+ VkSampler sampler;
+
+ void init(Device_Vk* dev, const Sampler_State& s);
+ void destroy(Device_Vk* dev) override;
+
+ static VkFilter get_filter(Filter_Mode mode);
+ static VkSamplerMipmapMode get_mipmap_mode(Filter_Mode mode);
+ static VkSamplerAddressMode get_mode(Address_Mode mode);
+};
+
template<>
struct Hash_Function<Render_Pass>
{
@@ -639,6 +670,13 @@ struct Hash_Function<Vertex_Format_Id> {
};
template<>
+struct Hash_Function<Sampler_Id> {
+ size_t operator()(Sampler_Id id) const {
+ return id.index;
+ }
+};
+
+template<>
struct std::hash<Render_Pass> {
size_t operator()(const Render_Pass& rp) const {
return (size_t)fnv1a64((uint8_t*)&rp, sizeof rp);
@@ -717,10 +755,12 @@ struct Device_Vk : public Device {
max_vertex_formats
> vertex_formats;
Hash_Map<Shader_Id, Shader_Vk, max_shaders> shaders;
+ Hash_Map<Sampler_Id, Sampler_Vk, max_samplers> samplers;
uint32_t texture_count;
uint32_t buffer_count;
uint32_t vertex_format_count;
uint32_t shader_count;
+ uint32_t sampler_count;
Hash_Map<Render_Pass, Renderpass_Vk, max_rpos> rpo_cache;
Hash_Map<Pso_Key, Pipeline_Vk, max_pipelines> pso_cache;
@@ -734,6 +774,7 @@ struct Device_Vk : public Device {
Vertex_Format_Id create_vf(Shader_Vk& shader);
void destroy_vf(Vertex_Format_Id id);
Shader_Id alloc_shader();
+ Sampler_Id alloc_sampler();
void init_internal();
void deinit_internal();
@@ -874,6 +915,7 @@ bool Device_Vk::has_validation() {
void Device_Vk::find_exts(const char** exts, int& count) {
app->get_vk_exts(exts, count);
+ exts[count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
#ifdef DEBUG
exts[count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
#endif
@@ -1023,11 +1065,14 @@ VkPhysicalDevice get_phys_dev(Device_Vk* d, Swap_Cap* sc) {
void Device_Vk::create_dev(Swap_Cap* swap_cap) {
const float priority = 0.0f;
VkDeviceQueueCreateInfo qi{};
+ VkPhysicalDeviceCustomBorderColorFeaturesEXT border{};
VkDeviceCreateInfo di{};
VkPhysicalDeviceFeatures pdf{};
VkResult r;
phys_dev = get_phys_dev(this, swap_cap);
vkGetPhysicalDeviceMemoryProperties(phys_dev, &mem_props);
+ border.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT;
+ border.customBorderColors = true;
qi.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
qi.queueFamilyIndex = queue_index;
qi.queueCount = 1;
@@ -1039,7 +1084,7 @@ void Device_Vk::create_dev(Swap_Cap* swap_cap) {
di.enabledExtensionCount =
sizeof device_exts / sizeof *device_exts;
di.ppEnabledExtensionNames = device_exts;
- di.pNext = 0;
+ di.pNext = &border;
r = vkCreateDevice(
phys_dev,
&di,
@@ -1064,6 +1109,8 @@ void Device_Vk::init_internal() {
vertex_format_count = 1;
shaders.init();
shader_count = 1;
+ samplers.init();
+ sampler_count = 1;
rpo_cache.init();
pso_cache.init();
shader_loader.init(this);
@@ -1545,6 +1592,7 @@ Texture& Device::get_texture(Texture_Id id) {
Texture_Id Device_Vk::alloc_texture() {
Texture_Vk tex{};
Texture_Id id(texture_count++);
+ tex.id = id;
textures.set(id, tex);
return id;
}
@@ -1552,6 +1600,7 @@ Texture_Id Device_Vk::alloc_texture() {
Buffer_Id Device_Vk::alloc_buffer() {
Buffer_Vk buf{};
Buffer_Id id(buffer_count++);
+ buf.id = id;
buffers.set(id, buf);
return id;
}
@@ -1577,11 +1626,17 @@ void Device_Vk::destroy_vf(Vertex_Format_Id id) {
Shader_Id Device_Vk::alloc_shader() {
Shader_Vk buf{};
Shader_Id id(shader_count++);
- assert(id.index < max_shaders);
shaders.set(id, buf);
return id;
}
+Sampler_Id Device_Vk::alloc_sampler() {
+ Sampler_Vk s{};
+ Sampler_Id id(sampler_count++);
+ samplers.set(id, s);
+ return id;
+}
+
void Device::destroy_texture(Texture_Id id) {
Device_Vk* dev = (Device_Vk*)this;
dev->queue_destroy((Texture_Vk*)&dev->get_texture(id));
@@ -1615,6 +1670,16 @@ void Context::submit(
VK_PIPELINE_BIND_POINT_GRAPHICS,
pso.pip
);
+ vkCmdBindDescriptorSets(
+ ctx->cb,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ pso.lay,
+ 0,
+ 1,
+ &pso.dset,
+ 0,
+ 0
+ );
for (binding = draw.verts; binding->id; binding++) {
VkBuffer buf = ((Buffer_Vk*)&dev->get_buffer(binding->id))->buf;
VkDeviceSize offset = (VkDeviceSize)binding->offset;
@@ -2101,6 +2166,143 @@ void Pipeline_Vk::init_blending(
info.pColorBlendState = &bi;
}
+void Pipeline_Vk::init_descriptors(
+ Device_Vk* dev,
+ const Pipeline& desc,
+ int count
+) {
+ int sampler_count = 0, cbuffer_count = 0;
+ const Descriptor* d = desc.descriptors;
+ Shader_Vk& shader = *(Shader_Vk*)&dev->get_shader(desc.shader);
+ VkDescriptorPoolSize sizes[4];
+ VkResult r;
+ int size_count = 0, i;
+ for (; d; d = d->next) {
+ switch (d->type) {
+ case Descriptor::Type::texture:
+ sampler_count++;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ if (sampler_count) {
+ int idx = size_count++;
+ sizes[idx] = {
+ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .descriptorCount = (uint32_t)sampler_count
+ };
+ }
+ if (cbuffer_count) {
+ int idx = size_count++;
+ sizes[idx] = {
+ .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ .descriptorCount = (uint32_t)cbuffer_count
+ };
+ }
+ {
+ VkDescriptorPoolCreateInfo di{};
+ di.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ di.poolSizeCount = (uint32_t)size_count;
+ di.pPoolSizes = sizes;
+ di.maxSets = (uint32_t)count;
+ r = vkCreateDescriptorPool(dev->dev, &di, &dev->ac, &dp);
+ if (r != VK_SUCCESS) {
+ print_err("Failed to create a descriptor pool.\n");
+ pbreak(r);
+ }
+ }
+ {
+ VkDescriptorSetLayoutBinding* descs =
+ (VkDescriptorSetLayoutBinding*)heap_alloc(
+ dev->heap,
+ count * sizeof *descs
+ );
+ VkDescriptorSetLayoutCreateInfo di{};
+ VkDescriptorSetAllocateInfo da{};
+ memset(descs, 0, count * sizeof *descs);
+ Descriptor* src = desc.descriptors;
+ for (i = count - 1; i >= 0; i--) {
+ int j, stage;
+ auto& dst = descs[i];
+ switch (src->type) {
+ case Descriptor::Type::texture:
+ dst.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ dst.binding = src->slot;
+ dst.descriptorCount = 1;
+ dst.stageFlags = 0;
+ stage = shader.descriptor_stage(src->slot);
+ for (j = 0; j < shader_type_count; j++) {
+ if (stage & (1 << j)) {
+ dst.stageFlags |= Shader_Vk::stage((Shader_Type)j);
+ }
+ }
+ src = src->next;
+ }
+ di.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ di.bindingCount = (uint32_t)count;
+ di.pBindings = descs;
+ r = vkCreateDescriptorSetLayout(
+ dev->dev,
+ &di,
+ &dev->ac,
+ &dlay
+ );
+ if (r != VK_SUCCESS) {
+ print_err("Failed to create descriptor set layout.\n");
+ pbreak(r);
+ }
+ da.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ da.descriptorPool = dp;
+ da.descriptorSetCount = 1;
+ da.pSetLayouts = &dlay;
+ r = vkAllocateDescriptorSets(
+ dev->dev,
+ &da,
+ &dset
+ );
+ /* todo this should be refactored to allow
+ * swapping the resources without recreating pipelines */
+ src = desc.descriptors;
+ for (i = 0; i < count; i++) {
+ VkDescriptorImageInfo img{};
+ VkWriteDescriptorSet wd{};
+ wd.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ wd.dstSet = dset;
+ wd.dstBinding = src->slot;
+ wd.dstArrayElement = 0;
+ wd.descriptorCount = 1;
+
+ switch (src->type) {
+ case Descriptor::Type::texture: {
+ Texture_Descriptor* td = (Texture_Descriptor*)src;
+ assert(td->texture);
+ assert(td->sampler);
+ Texture_Vk& t = *(Texture_Vk*)&dev->get_texture(td->texture);
+ Sampler_Vk& s = *(Sampler_Vk*)&dev->samplers[td->sampler];
+ img.imageView = t.view;
+ img.sampler = s.sampler;
+ img.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ wd.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ wd.pImageInfo = &img;
+ } break;
+ default:
+ assert(0);
+ break;
+ }
+ vkUpdateDescriptorSets(dev->dev, 1, &wd, 0, 0);
+ src = src->next;
+ }
+ heap_free(dev->heap, descs);
+ }
+}
+
void Pipeline_Vk::init_layout(
Device_Vk* dev,
const Pipeline& desc
@@ -2108,9 +2310,15 @@ void Pipeline_Vk::init_layout(
VkResult r;
VkPipelineLayoutCreateInfo li{};
(void)desc;
+ int desc_count = desc.count_descriptors();
+ int set_count = desc_count? 1: 0;
+ if (set_count) {
+ init_descriptors(dev, desc, desc_count);
+ }
li.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
- li.setLayoutCount = 0;
- li.pushConstantRangeCount = 0; /* todo */
+ li.setLayoutCount = set_count;
+ li.pSetLayouts = &dlay;
+ li.pushConstantRangeCount = 0;
r = vkCreatePipelineLayout(
dev->dev,
&li,
@@ -2159,8 +2367,10 @@ void Pipeline_Vk::init(Device_Vk* dev, const Pso_Key& key) {
}
void Pipeline_Vk::destroy(Device_Vk* dev) {
- vkDestroyPipeline(dev->dev, pip, &dev->ac);
+ vkDestroyDescriptorPool(dev->dev, dp, &dev->ac);
+ vkDestroyDescriptorSetLayout(dev->dev, dlay, &dev->ac);
vkDestroyPipelineLayout(dev->dev, lay, &dev->ac);
+ vkDestroyPipeline(dev->dev, pip, &dev->ac);
}
int Vertex_Format_Vk::svariable_type_size(SVariable_Type type) {
@@ -2365,6 +2575,7 @@ void Shader_Vk::destroy(Device_Vk* dev) {
if (modules[i])
vkDestroyShaderModule(dev->dev, modules[i], &dev->ac);
vfd.destroy(dev);
+ heap_free(dev->heap, descs);
dev->destroy_vf(vf);
dev->shaders.remove(id);
}
@@ -2385,6 +2596,25 @@ int Shader::attribute_index(const char* name) {
return idx;
}
+int Shader::descriptor_index(const char* name) {
+ int idx;
+ Shader_Vk* sh = (Shader_Vk*)this;
+ idx = sh->find_descriptor(name);
+ if (idx < 0 || !sh->descs[idx].name[0]) return -1;
+ return idx;
+}
+
+int Shader::descriptor_stage(int slot) {
+ Shader_Vk* sh = (Shader_Vk*)this;
+ int i;
+ for (i = 0; i < sh->desc_count; i++) {
+ if (sh->descs[i].slot == slot) {
+ return sh->descs[i].stage;
+ }
+ }
+ return 0;
+}
+
void Buffer_Vk::init(
Device_Vk* dev,
int flags,
@@ -2481,6 +2711,20 @@ Shader& Device::get_shader(Shader_Id id) {
return ((Device_Vk*)this)->shaders[id];
}
+Sampler_Id Device::create_sampler(const Sampler_State& state) {
+ Device_Vk* dev = (Device_Vk*)this;
+ Sampler_Id id = dev->alloc_sampler();
+ Sampler_Vk& s = dev->samplers[id];
+ s.init(dev, state);
+ return id;
+}
+
+void Device::destroy_sampler(Sampler_Id id) {
+ Device_Vk* dev = (Device_Vk*)this;
+ Sampler_Vk& s = dev->samplers[id];
+ dev->queue_destroy(&s);
+}
+
void Shader_Loader::init(Device_Vk* d) {
dev = d;
}
@@ -2509,6 +2753,20 @@ void Shader_Loader::unload(Asset* a) {
dev->queue_destroy(sh);
}
+int Shader_Vk::find_descriptor(const char* name) {
+ int i;
+ int bucket = (int)(hash_string(name) % desc_count);
+ for (i = 0; i < desc_count; i++) {
+ Desc& desc = descs[bucket];
+ if (
+ !desc.name[0] ||
+ !strcmp(desc.name, name)
+ ) return bucket;
+ bucket = (bucket + 1) % desc_count;
+ }
+ return -1;
+}
+
bool Shader_Vk::init(Device_Vk* dev, Pack_File* f) {
char magic[4];
int binding_count, target_count, i;
@@ -2522,8 +2780,9 @@ bool Shader_Vk::init(Device_Vk* dev, Pack_File* f) {
pack_read(f, &type, 4);
pack_read(f, &binding_count, 4);
pack_read(f, &target_count, 4);
- vfd.binding_count = binding_count;
+ pack_read(f, &desc_count, 4);
assert(binding_count);
+ vfd.binding_count = binding_count;
if (!vfd.init(dev, f))
return false;
vf = dev->create_vf(*this);
@@ -2532,6 +2791,11 @@ bool Shader_Vk::init(Device_Vk* dev, Pack_File* f) {
32 * target_count,
seek_rel_cur
);
+ descs = (Desc*)heap_alloc(
+ dev->heap,
+ desc_count * sizeof *descs
+ );
+ pack_read(f, descs, desc_count * sizeof *descs);
for (i = 0; i < shader_type_count; i++) {
int o, s;
pack_read(f, &o, 4);
@@ -2655,6 +2919,7 @@ Asset* Texture_Loader::load(Arena* a, Arena* s, Pack_File* f) {
int w, h;
size_t size;
Texture_Format fmt;
+ (void)a;
pack_read(f, magic, 4);
pack_read(f, &w, 4);
pack_read(f, &h, 4);
@@ -2679,7 +2944,7 @@ Asset* Texture_Loader::load(Arena* a, Arena* s, Pack_File* f) {
void Texture_Loader::unload(Asset* a) {
Texture_Vk* tex = (Texture_Vk*)a;
- tex->destroy(dev);
+ dev->destroy_texture(tex->id);
}
void Texture_Vk::destroy(Device_Vk* dev) {
@@ -2691,3 +2956,73 @@ void Texture_Vk::destroy(Device_Vk* dev) {
dev->textures.remove(id);
}
+VkFilter Sampler_Vk::get_filter(Filter_Mode mode) {
+ switch (mode) {
+ case Filter_Mode::point: return VK_FILTER_NEAREST;
+ case Filter_Mode::linear: return VK_FILTER_LINEAR;
+ }
+ assert(0);
+ return (VkFilter)0;
+}
+
+VkSamplerMipmapMode Sampler_Vk::get_mipmap_mode(
+ Filter_Mode mode
+) {
+ switch (mode) {
+ case Filter_Mode::point:
+ return VK_SAMPLER_MIPMAP_MODE_NEAREST;
+ case Filter_Mode::linear:
+ return VK_SAMPLER_MIPMAP_MODE_LINEAR;
+ }
+ assert(0);
+ return (VkSamplerMipmapMode)0;
+}
+
+VkSamplerAddressMode Sampler_Vk::get_mode(
+ Address_Mode mode
+) {
+ switch (mode) {
+ case Address_Mode::repeat:
+ return VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ case Address_Mode::mirror:
+ return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
+ case Address_Mode::clamp:
+ return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ case Address_Mode::border:
+ return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+ }
+ assert(0);
+ return (VkSamplerAddressMode)0;
+}
+
+void Sampler_Vk::init(Device_Vk* dev, const Sampler_State& s) {
+ VkSamplerCreateInfo si{};
+ VkSamplerCustomBorderColorCreateInfoEXT bi{};
+ VkClearColorValue col{};
+ VkResult r;
+ col.float32[0] = s.border[0];
+ col.float32[1] = s.border[1];
+ col.float32[2] = s.border[2];
+ col.float32[3] = s.border[3];
+ si.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+ si.magFilter = get_filter(s.mag);
+ si.minFilter = get_filter(s.min);
+ si.mipmapMode = get_mipmap_mode(s.mip);
+ si.addressModeU = get_mode(s.address_u);
+ si.addressModeV = get_mode(s.address_v);
+ si.addressModeW = get_mode(s.address_w);
+ si.borderColor = VK_BORDER_COLOR_FLOAT_CUSTOM_EXT;
+ si.pNext = &bi;
+ bi.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT;
+ bi.customBorderColor = col;
+ bi.format = VK_FORMAT_R32G32B32A32_SFLOAT;
+ r = vkCreateSampler(dev->dev, &si, &dev->ac, &sampler);
+ if (r != VK_SUCCESS) {
+ print_err("Failed to create a sampler.\n");
+ pbreak(r);
+ }
+}
+
+void Sampler_Vk::destroy(Device_Vk* dev) {
+ vkDestroySampler(dev->dev, sampler, &dev->ac);
+}