mirror of https://github.com/snes9xgit/snes9x.git
Clean up whitespace and remove a few test files.
This commit is contained in:
parent
259dfd07ae
commit
ed37f930aa
|
@ -19,7 +19,7 @@ void trim(string_view &view)
|
|||
{
|
||||
while (view.length() > 0 && isspace(view.at(0)))
|
||||
view.remove_prefix(1);
|
||||
|
||||
|
||||
while (view.length() > 0 && isspace(view.at(view.length() - 1)))
|
||||
view.remove_suffix(1);
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ string trim(const string& str)
|
|||
return string(sv);
|
||||
}
|
||||
|
||||
int get_significant_digits(const string_view &view)
|
||||
int get_significant_digits(const string_view &view)
|
||||
{
|
||||
auto pos = view.rfind('.');
|
||||
if (pos == string_view::npos)
|
||||
|
@ -58,13 +58,13 @@ vector<string> split_string_quotes(const string_view &view)
|
|||
if (indexb == string::npos)
|
||||
break;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
indexb = view.find_first_of("\t\r\n ", indexa);
|
||||
if (indexb == string::npos)
|
||||
indexb = view.size();
|
||||
}
|
||||
|
||||
|
||||
if (indexb > indexa)
|
||||
tokens.push_back(string{view.substr(indexa, indexb - indexa)});
|
||||
pos = indexb + 1;
|
||||
|
@ -78,7 +78,7 @@ vector<string> split_string(const string_view &str, unsigned char delim)
|
|||
vector<string> tokens;
|
||||
size_t pos = 0;
|
||||
size_t index;
|
||||
|
||||
|
||||
while (pos < str.length())
|
||||
{
|
||||
index = str.find(delim, pos);
|
||||
|
@ -86,7 +86,7 @@ vector<string> split_string(const string_view &str, unsigned char delim)
|
|||
{
|
||||
if (pos < str.length())
|
||||
{
|
||||
tokens.push_back(string{str.substr(pos)});
|
||||
tokens.push_back(string{str.substr(pos)});
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -97,7 +97,7 @@ vector<string> split_string(const string_view &str, unsigned char delim)
|
|||
}
|
||||
|
||||
pos = index + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ bool ends_with(const string &str, const string &ext)
|
|||
auto icmp = [](const unsigned char a, const unsigned char b) -> bool {
|
||||
return std::tolower(a) == std::tolower(b);
|
||||
};
|
||||
|
||||
|
||||
return std::equal(ext.crbegin(), ext.crend(), str.crbegin(), icmp);
|
||||
}
|
||||
|
||||
|
|
|
@ -259,7 +259,7 @@ void SlangPreset::gather_parameters()
|
|||
map.insert({ p.id, p });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
parameters.clear();
|
||||
for (auto &p : map)
|
||||
parameters.push_back(p.second);
|
||||
|
@ -344,8 +344,8 @@ void SlangPreset::print()
|
|||
printf(" Samplers: %zu\n", s.samplers.size());
|
||||
for (auto &sampler : s.samplers)
|
||||
{
|
||||
const char *strings[] =
|
||||
{
|
||||
const char *strings[] =
|
||||
{
|
||||
"Previous Frame",
|
||||
"Pass",
|
||||
"Pass Feedback",
|
||||
|
@ -426,7 +426,7 @@ bool SlangPreset::match_sampler_semantic(const string &name, int pass, SlangShad
|
|||
specifier = -1;
|
||||
return true;
|
||||
}
|
||||
else if (name == "Source")
|
||||
else if (name == "Source")
|
||||
{
|
||||
type = SlangShader::Sampler::Type::Pass;
|
||||
specifier = pass - 1;
|
||||
|
@ -452,7 +452,7 @@ bool SlangPreset::match_sampler_semantic(const string &name, int pass, SlangShad
|
|||
type = SlangShader::Sampler::Type::Lut;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < passes.size(); i++)
|
||||
{
|
||||
|
@ -462,7 +462,7 @@ bool SlangPreset::match_sampler_semantic(const string &name, int pass, SlangShad
|
|||
specifier = i;
|
||||
return true;
|
||||
}
|
||||
else if (passes[i].alias + "Feedback" == name)
|
||||
else if (passes[i].alias + "Feedback" == name)
|
||||
{
|
||||
type = SlangShader::Sampler::Type::PassFeedback;
|
||||
specifier = i;
|
||||
|
@ -491,7 +491,7 @@ bool SlangPreset::match_buffer_semantic(const string &name, int pass, SlangShade
|
|||
type = SlangShader::Uniform::Type::MVP;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (name == "FrameCount")
|
||||
{
|
||||
type = SlangShader::Uniform::Type::FrameCount;
|
||||
|
@ -518,7 +518,7 @@ bool SlangPreset::match_buffer_semantic(const string &name, int pass, SlangShade
|
|||
|
||||
if (name.compare(prefix.length(), 4, "Size") != 0)
|
||||
return false;
|
||||
|
||||
|
||||
if (prefix.length() + 4 < name.length())
|
||||
specifier = std::stoi(name.substr(prefix.length() + 4));
|
||||
|
||||
|
@ -530,7 +530,7 @@ bool SlangPreset::match_buffer_semantic(const string &name, int pass, SlangShade
|
|||
type = SlangShader::Uniform::Type::PassSize;
|
||||
specifier = -1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (match("Source"))
|
||||
{
|
||||
type = SlangShader::Uniform::Type::PassSize;
|
||||
|
@ -573,7 +573,7 @@ bool SlangPreset::match_buffer_semantic(const string &name, int pass, SlangShade
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (size_t i = 0; i < textures.size(); i++)
|
||||
{
|
||||
if (match(textures[i].id))
|
||||
|
@ -636,7 +636,7 @@ bool SlangPreset::introspect_shader(SlangShader &shader, int pass, SlangShader::
|
|||
{
|
||||
shader.push_constant_block_size = 0;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
auto &pcb = res.push_constant_buffers[0];
|
||||
auto &pcb_type = cross.get_type(pcb.base_type_id);
|
||||
|
@ -659,7 +659,7 @@ bool SlangPreset::introspect_shader(SlangShader &shader, int pass, SlangShader::
|
|||
if (!exists(uniform))
|
||||
shader.uniforms.push_back(uniform);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
printf("%s: Failed to match push constant semantic: \"%s\"\n", shader.filename.c_str(), name.c_str());
|
||||
}
|
||||
|
@ -670,7 +670,7 @@ bool SlangPreset::introspect_shader(SlangShader &shader, int pass, SlangShader::
|
|||
{
|
||||
shader.ubo_size = 0;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
auto &ubo = res.uniform_buffers[0];
|
||||
auto &ubo_type = cross.get_type(ubo.base_type_id);
|
||||
|
@ -694,7 +694,7 @@ bool SlangPreset::introspect_shader(SlangShader &shader, int pass, SlangShader::
|
|||
if (!exists(uniform))
|
||||
shader.uniforms.push_back(uniform);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
printf("%s: Failed to match uniform buffer semantic: \"%s\"\n", shader.filename.c_str(), name.c_str());
|
||||
}
|
||||
|
@ -706,7 +706,7 @@ bool SlangPreset::introspect_shader(SlangShader &shader, int pass, SlangShader::
|
|||
printf("No sampled images found in fragment shader.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (res.sampled_images.size() > 0 && stage == SlangShader::Stage::Vertex)
|
||||
{
|
||||
printf("Sampled image found in vertex shader.\n");
|
||||
|
@ -725,7 +725,7 @@ bool SlangPreset::introspect_shader(SlangShader &shader, int pass, SlangShader::
|
|||
int binding = cross.get_decoration(image.id, spv::DecorationBinding);
|
||||
shader.samplers.push_back({ semantic_type, specifier, binding });
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
printf("%s: Failed to match sampler semantic: \"%s\"\n", shader.filename.c_str(), image.name.c_str());
|
||||
return false;
|
||||
|
@ -740,7 +740,7 @@ bool SlangPreset::introspect_shader(SlangShader &shader, int pass, SlangShader::
|
|||
Introspect all of preset's shaders.
|
||||
*/
|
||||
bool SlangPreset::introspect()
|
||||
{
|
||||
{
|
||||
for (size_t i = 0; i < passes.size(); i++)
|
||||
{
|
||||
if (!introspect_shader(passes[i], i, SlangShader::Stage::Vertex))
|
||||
|
@ -752,7 +752,7 @@ bool SlangPreset::introspect()
|
|||
oldest_previous_frame = 0;
|
||||
uses_feedback = false;
|
||||
last_pass_uses_feedback = false;
|
||||
|
||||
|
||||
for (auto &p : passes)
|
||||
{
|
||||
for (auto &s : p.samplers)
|
||||
|
@ -772,7 +772,7 @@ bool SlangPreset::introspect()
|
|||
}
|
||||
|
||||
bool SlangPreset::save_to_file(std::string filename)
|
||||
{
|
||||
{
|
||||
std::ofstream out(filename);
|
||||
if (!out.is_open())
|
||||
return false;
|
||||
|
@ -837,6 +837,6 @@ bool SlangPreset::save_to_file(std::string filename)
|
|||
}
|
||||
|
||||
out.close();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
|
@ -17,7 +17,7 @@ static std::string trim_comments(std::string str)
|
|||
for (auto &comment : { "//", "#" })
|
||||
{
|
||||
auto location = str.rfind(comment);
|
||||
|
||||
|
||||
if (location != std::string::npos)
|
||||
str = str.substr(0, location);
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ bool IniFile::load_file(std::string filename)
|
|||
{
|
||||
if (file.eof())
|
||||
break;
|
||||
|
||||
|
||||
std::getline(file, line);
|
||||
|
||||
line = trim(line);
|
||||
|
@ -75,7 +75,7 @@ bool IniFile::load_file(std::string filename)
|
|||
{
|
||||
auto left_side = trim_quotes(trim(line.substr(0, equals)));
|
||||
auto right_side = trim_quotes(trim(line.substr(equals + 1)));
|
||||
|
||||
|
||||
keys.insert_or_assign(left_side, std::make_pair(right_side, filename));
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ std::string IniFile::get_source(std::string key)
|
|||
auto it = keys.find(key);
|
||||
if (it == keys.end())
|
||||
return "";
|
||||
|
||||
|
||||
return it->second.second;
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,7 @@ bool IniFile::get_bool(std::string key, bool default_value = false)
|
|||
std::string lower = it->second.first;
|
||||
for (auto &c : lower)
|
||||
c = tolower(c);
|
||||
|
||||
|
||||
const char *true_strings[] = { "true", "1", "yes", "on"};
|
||||
for (auto &s : true_strings)
|
||||
if (lower == s)
|
||||
|
|
|
@ -16,10 +16,10 @@ int main(int argc, char **argv)
|
|||
printf("Failed to load %s\n", argv[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
preset.introspect();
|
||||
|
||||
preset.print();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
struct SlangShader
|
||||
{
|
||||
struct Parameter
|
||||
struct Parameter
|
||||
{
|
||||
std::string name;
|
||||
std::string id;
|
||||
|
@ -58,7 +58,7 @@ struct SlangShader
|
|||
int binding;
|
||||
};
|
||||
|
||||
enum class Stage
|
||||
enum class Stage
|
||||
{
|
||||
Vertex,
|
||||
Fragment
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
#include <gtkmm.h>
|
||||
#include <gdk/gdkx.h>
|
||||
#include "vk2d.hpp"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
XInitThreads();
|
||||
auto application = Gtk::Application::create(argc, argv, "org.bearoso.vulkantest");
|
||||
|
||||
Gtk::Window window;
|
||||
Gtk::Button button;
|
||||
Gtk::DrawingArea drawingarea;
|
||||
Gtk::VBox vbox;
|
||||
|
||||
window.set_title("Vulkan Test");
|
||||
window.set_events(Gdk::EventMask::ALL_EVENTS_MASK);
|
||||
button.set_label("Close");
|
||||
|
||||
vbox.pack_start(drawingarea, true, true, 0);
|
||||
vbox.pack_start(button, false, false, 0);
|
||||
vbox.set_spacing(5);
|
||||
button.set_hexpand(false);
|
||||
button.set_halign(Gtk::ALIGN_END);
|
||||
|
||||
window.add(vbox);
|
||||
window.set_border_width(5);
|
||||
vbox.show_all();
|
||||
|
||||
button.signal_clicked().connect([&] {
|
||||
window.close();
|
||||
});
|
||||
|
||||
window.resize(640, 480);
|
||||
window.show_all();
|
||||
|
||||
Window xid = gdk_x11_window_get_xid(drawingarea.get_window()->gobj());
|
||||
Display *dpy = gdk_x11_display_get_xdisplay(drawingarea.get_display()->gobj());
|
||||
|
||||
vk2d vk2d;
|
||||
|
||||
vk2d.init_xlib_instance();
|
||||
vk2d.attach(dpy, xid);
|
||||
vk2d.init_device();
|
||||
|
||||
drawingarea.signal_configure_event().connect([&](GdkEventConfigure *event) {
|
||||
vk2d.recreate_swapchain();
|
||||
return false;
|
||||
});
|
||||
|
||||
window.signal_key_press_event().connect([&](GdkEventKey *key) -> bool {
|
||||
printf ("Key press %d\n", key->keyval);
|
||||
return false;
|
||||
}, false);
|
||||
|
||||
window.signal_key_release_event().connect([&](GdkEventKey *key) -> bool {
|
||||
printf ("Key release %d\n", key->keyval);
|
||||
return false;
|
||||
}, false);
|
||||
|
||||
drawingarea.set_app_paintable(true);
|
||||
|
||||
drawingarea.signal_draw().connect([&](const Cairo::RefPtr<Cairo::Context> &context) -> bool {
|
||||
return true;
|
||||
});
|
||||
|
||||
auto id = Glib::signal_idle().connect([&]{
|
||||
vk2d.draw();
|
||||
vk2d.wait_idle();
|
||||
return true;
|
||||
});
|
||||
|
||||
window.signal_delete_event().connect([&](GdkEventAny *event) {
|
||||
id.disconnect();
|
||||
return false;
|
||||
});
|
||||
|
||||
application->run(window);
|
||||
|
||||
return 0;
|
||||
}
|
641
vulkan/vk2d.cpp
641
vulkan/vk2d.cpp
|
@ -1,641 +0,0 @@
|
|||
#include "vk2d.hpp"
|
||||
|
||||
#include <glslang/Include/BaseTypes.h>
|
||||
#include <glslang/Public/ShaderLang.h>
|
||||
#include <glslang/SPIRV/GlslangToSpv.h>
|
||||
#include <glslang/Include/ResourceLimits.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
static const char *vertex_shader = R"(
|
||||
#version 450
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
|
||||
vec2 positions[3] = vec2[](
|
||||
vec2(0.0, -0.5),
|
||||
vec2(0.5, 0.5),
|
||||
vec2(-0.5, 0.5)
|
||||
);
|
||||
|
||||
vec3 colors[3] = vec3[](
|
||||
vec3(1.0, 0.0, 0.0),
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
vec3(0.0, 0.0, 1.0)
|
||||
);
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||
fragColor = colors[gl_VertexIndex];
|
||||
}
|
||||
)";
|
||||
|
||||
static const char *fragment_shader = R"(
|
||||
#version 450
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(fragColor, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
bool vk2d::dispatcher_initialized = false;
|
||||
|
||||
vk2d::vk2d()
|
||||
{
|
||||
instance = nullptr;
|
||||
surface = nullptr;
|
||||
|
||||
if (!dispatcher_initialized)
|
||||
{
|
||||
vk::DynamicLoader *dl = new vk::DynamicLoader;
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl->getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
||||
dispatcher_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
vk2d::~vk2d()
|
||||
{
|
||||
deinit();
|
||||
}
|
||||
|
||||
bool vk2d::init_device()
|
||||
{
|
||||
if (!instance || !surface)
|
||||
return false;
|
||||
|
||||
choose_physical_device();
|
||||
create_device();
|
||||
create_sync_objects();
|
||||
create_swapchain();
|
||||
create_render_pass();
|
||||
create_pipeline();
|
||||
create_framebuffers();
|
||||
create_command_buffers();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
||||
bool vk2d::init_xlib_instance()
|
||||
{
|
||||
if (instance)
|
||||
return true;
|
||||
|
||||
std::vector<const char *> extensions = { VK_KHR_XLIB_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_EXTENSION_NAME };
|
||||
vk::ApplicationInfo ai({}, {}, {}, {}, VK_API_VERSION_1_0);
|
||||
vk::InstanceCreateInfo ci({}, &ai, {}, extensions);
|
||||
|
||||
auto rv = vk::createInstance(ci);
|
||||
if (rv.result != vk::Result::eSuccess)
|
||||
return false;
|
||||
|
||||
instance = rv.value;
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
|
||||
return true;
|
||||
}
|
||||
|
||||
void vk2d::attach(Display *dpy, Window xid)
|
||||
{
|
||||
if (surface)
|
||||
{
|
||||
instance.destroySurfaceKHR(surface);
|
||||
surface = nullptr;
|
||||
}
|
||||
|
||||
vk::XlibSurfaceCreateInfoKHR sci({}, dpy, xid);
|
||||
auto rv = instance.createXlibSurfaceKHR(sci);
|
||||
VK_CHECK(rv.result);
|
||||
surface = rv.value;
|
||||
}
|
||||
#endif // VK_USE_PLATFORM_XLIB_KHR
|
||||
|
||||
bool vk2d::create_instance()
|
||||
{
|
||||
std::vector<const char *> extensions = { VK_KHR_XLIB_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_EXTENSION_NAME };
|
||||
|
||||
vk::ApplicationInfo ai({}, {}, {}, {}, VK_API_VERSION_1_1);
|
||||
vk::InstanceCreateInfo ci({}, &ai, {}, {}, extensions.size(), extensions.data());
|
||||
|
||||
auto rv = vk::createInstance(ci);
|
||||
VK_CHECK(rv.result);
|
||||
instance = rv.value;
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
|
||||
return true;
|
||||
}
|
||||
|
||||
void vk2d::deinit()
|
||||
{
|
||||
destroy_swapchain();
|
||||
|
||||
frame_queue.clear();
|
||||
|
||||
if (command_pool)
|
||||
device.destroyCommandPool(command_pool);
|
||||
|
||||
if (device)
|
||||
device.destroy();
|
||||
|
||||
if (surface)
|
||||
instance.destroySurfaceKHR(surface);
|
||||
|
||||
if (instance)
|
||||
instance.destroy();
|
||||
}
|
||||
|
||||
void vk2d::destroy_swapchain()
|
||||
{
|
||||
if (device)
|
||||
{
|
||||
VK_CHECK(device.waitIdle());
|
||||
}
|
||||
swapchain.framebuffers.clear();
|
||||
swapchain.views.clear();
|
||||
|
||||
device.freeCommandBuffers(command_pool, swapchain.command_buffers);
|
||||
|
||||
if (graphics_pipeline)
|
||||
device.destroyPipeline(graphics_pipeline);
|
||||
|
||||
if (render_pass)
|
||||
device.destroyRenderPass(render_pass);
|
||||
|
||||
if (pipeline_layout)
|
||||
device.destroyPipelineLayout(pipeline_layout);
|
||||
|
||||
if (swapchain.obj)
|
||||
{
|
||||
device.destroySwapchainKHR(swapchain.obj);
|
||||
swapchain.obj = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void vk2d::recreate_swapchain()
|
||||
{
|
||||
frame_queue_index = 0;
|
||||
destroy_swapchain();
|
||||
create_swapchain();
|
||||
create_render_pass();
|
||||
create_pipeline();
|
||||
create_framebuffers();
|
||||
create_command_buffers();
|
||||
}
|
||||
|
||||
void vk2d::choose_physical_device()
|
||||
{
|
||||
if (!surface)
|
||||
assert(0);
|
||||
|
||||
auto devices = instance.enumeratePhysicalDevices();
|
||||
VK_CHECK(devices.result);
|
||||
std::vector<vk::PhysicalDevice> candidates;
|
||||
for (auto &d : devices.value)
|
||||
{
|
||||
auto extension_properties = d.enumerateDeviceExtensionProperties();
|
||||
VK_CHECK(extension_properties.result);
|
||||
bool presentable = false;
|
||||
for (auto &e : extension_properties.value)
|
||||
{
|
||||
std::string name = e.extensionName;
|
||||
if (name == VK_KHR_SWAPCHAIN_EXTENSION_NAME)
|
||||
{
|
||||
presentable = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!presentable)
|
||||
continue;
|
||||
|
||||
auto queue_families = d.getQueueFamilyProperties();
|
||||
|
||||
for (size_t q = 0; q < queue_families.size(); q++)
|
||||
{
|
||||
if (queue_families[q].queueFlags & vk::QueueFlagBits::eGraphics)
|
||||
{
|
||||
graphics_queue_index = q;
|
||||
presentable = true;
|
||||
break;
|
||||
}
|
||||
|
||||
presentable = false;
|
||||
}
|
||||
|
||||
presentable = presentable && d.getSurfaceSupportKHR(graphics_queue_index, surface).value;
|
||||
|
||||
if (presentable)
|
||||
{
|
||||
printf("Using %s\n", (char *)d.getProperties().deviceName);
|
||||
physical_device = d;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
physical_device = nullptr;
|
||||
graphics_queue_index = -1;
|
||||
}
|
||||
|
||||
void vk2d::wait_idle()
|
||||
{
|
||||
if (device)
|
||||
{
|
||||
auto result = device.waitIdle();
|
||||
VK_CHECK(result);
|
||||
}
|
||||
}
|
||||
|
||||
void vk2d::draw()
|
||||
{
|
||||
auto &frame = frame_queue[frame_queue_index];
|
||||
uint32_t next;
|
||||
|
||||
VK_CHECK(device.waitForFences(1, &frame.fence.get(), true, UINT64_MAX));
|
||||
vk::ResultValue resval = device.acquireNextImageKHR(swapchain.obj, UINT64_MAX, frame.image_ready.get(), nullptr);
|
||||
|
||||
if (resval.result != vk::Result::eSuccess)
|
||||
{
|
||||
if (resval.result == vk::Result::eErrorOutOfDateKHR)
|
||||
{
|
||||
printf("Recreating swapchain\n");
|
||||
recreate_swapchain();
|
||||
return;
|
||||
}
|
||||
|
||||
VK_CHECK(resval.result);
|
||||
exit(1);
|
||||
}
|
||||
next = resval.value;
|
||||
|
||||
if (swapchain.frame_fence[next] > -1)
|
||||
{
|
||||
VK_CHECK(device.waitForFences(1, &frame_queue[next].fence.get(), true, UINT64_MAX));
|
||||
}
|
||||
swapchain.frame_fence[next] = frame_queue_index;
|
||||
|
||||
VK_CHECK(device.resetFences(1, &frame.fence.get()));
|
||||
|
||||
vk::PipelineStageFlags flags = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
||||
vk::SubmitInfo submit_info(frame.image_ready.get(),
|
||||
flags,
|
||||
swapchain.command_buffers[next],
|
||||
frame.render_finished.get());
|
||||
|
||||
VK_CHECK(queue.submit(submit_info, frame.fence.get()));
|
||||
|
||||
vk::PresentInfoKHR present_info(frame.render_finished.get(), swapchain.obj, next, {});
|
||||
|
||||
VK_CHECK(queue.presentKHR(present_info));
|
||||
|
||||
frame_queue_index = (frame_queue_index + 1) % frame_queue_size;
|
||||
}
|
||||
|
||||
void vk2d::create_device()
|
||||
{
|
||||
float queue_priority = 1.0f;
|
||||
std::vector<const char *> extension_names = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
|
||||
|
||||
vk::DeviceQueueCreateInfo dqci({}, graphics_queue_index, 1, &queue_priority);
|
||||
|
||||
vk::DeviceCreateInfo dci;
|
||||
dci.setPEnabledExtensionNames(extension_names);
|
||||
|
||||
std::vector<vk::DeviceQueueCreateInfo> pqci = {dqci};
|
||||
dci.setQueueCreateInfos(pqci);
|
||||
device = physical_device.createDevice(dci).value;
|
||||
|
||||
queue = device.getQueue(graphics_queue_index, 0);
|
||||
|
||||
vk::CommandPoolCreateInfo command_pool_info({}, graphics_queue_index);
|
||||
command_pool = device.createCommandPool(command_pool_info).value;
|
||||
}
|
||||
|
||||
void vk2d::create_swapchain()
|
||||
{
|
||||
if (!device || !surface)
|
||||
assert(0);
|
||||
|
||||
vk::SurfaceCapabilitiesKHR surface_caps = physical_device.getSurfaceCapabilitiesKHR(surface).value;
|
||||
swapchain.size = surface_caps.minImageCount;
|
||||
|
||||
vk::SwapchainCreateInfoKHR sci;
|
||||
sci
|
||||
.setSurface(surface)
|
||||
.setMinImageCount(swapchain.size)
|
||||
.setPresentMode(vk::PresentModeKHR::eFifo)
|
||||
.setImageFormat(vk::Format::eB8G8R8A8Unorm)
|
||||
.setImageExtent(surface_caps.currentExtent)
|
||||
.setImageColorSpace(vk::ColorSpaceKHR::eSrgbNonlinear)
|
||||
.setImageArrayLayers(1)
|
||||
.setImageSharingMode(vk::SharingMode::eExclusive)
|
||||
.setImageUsage(vk::ImageUsageFlagBits::eColorAttachment)
|
||||
.setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque)
|
||||
.setClipped(true);
|
||||
|
||||
if (swapchain.obj)
|
||||
sci.setOldSwapchain(swapchain.obj);
|
||||
|
||||
swapchain.obj = device.createSwapchainKHR(sci).value;
|
||||
swapchain.extents = surface_caps.currentExtent;
|
||||
swapchain.images = device.getSwapchainImagesKHR(swapchain.obj).value;
|
||||
|
||||
swapchain.views.resize(swapchain.size);
|
||||
for (size_t i = 0; i < swapchain.size; i++)
|
||||
{
|
||||
vk::ImageViewCreateInfo image_view_create_info;
|
||||
image_view_create_info
|
||||
.setImage(swapchain.images[i])
|
||||
.setViewType(vk::ImageViewType::e2D)
|
||||
.setFormat(vk::Format::eB8G8R8A8Unorm)
|
||||
.setComponents(vk::ComponentMapping())
|
||||
.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
|
||||
swapchain.views[i] = device.createImageViewUnique(image_view_create_info).value;
|
||||
}
|
||||
|
||||
swapchain.frame_fence.resize(swapchain.size);
|
||||
for (auto &f : swapchain.frame_fence)
|
||||
f = -1;
|
||||
|
||||
}
|
||||
|
||||
void vk2d::create_sync_objects()
|
||||
{
|
||||
frame_queue.resize(frame_queue_size);
|
||||
for (size_t i = 0; i < frame_queue_size; i++)
|
||||
{
|
||||
vk::SemaphoreCreateInfo semaphore_create_info;
|
||||
frame_queue[i].image_ready = device.createSemaphoreUnique(semaphore_create_info).value;
|
||||
frame_queue[i].render_finished = device.createSemaphoreUnique(semaphore_create_info).value;
|
||||
|
||||
vk::FenceCreateInfo fence_create_info(vk::FenceCreateFlagBits::eSignaled);
|
||||
frame_queue[i].fence = device.createFenceUnique(fence_create_info).value;
|
||||
}
|
||||
frame_queue_index = 0;
|
||||
}
|
||||
|
||||
namespace glslang
|
||||
{
|
||||
extern const TBuiltInResource DefaultTBuiltInResource;
|
||||
}
|
||||
|
||||
void vk2d::create_shader_modules()
|
||||
{
|
||||
glslang::InitializeProcess();
|
||||
EShMessages message_flags = (EShMessages)(EShMsgDefault | EShMsgVulkanRules | EShMsgSpvRules);
|
||||
|
||||
glslang::TShader vertex(EShLangVertex);
|
||||
glslang::TShader fragment(EShLangFragment);
|
||||
|
||||
vertex.setStrings(&vertex_shader, 1);
|
||||
fragment.setStrings(&fragment_shader, 1);
|
||||
|
||||
vertex.parse(&glslang::DefaultTBuiltInResource, 450, true, message_flags);
|
||||
fragment.parse(&glslang::DefaultTBuiltInResource, 450, true, message_flags);
|
||||
|
||||
glslang::TProgram vertex_program;
|
||||
glslang::TProgram fragment_program;
|
||||
|
||||
vertex_program.addShader(&vertex);
|
||||
fragment_program.addShader(&fragment);
|
||||
|
||||
vertex_program.link(message_flags);
|
||||
fragment_program.link(message_flags);
|
||||
|
||||
auto log = [](const char *msg)
|
||||
{
|
||||
if (msg != nullptr && msg[0] != '\0')
|
||||
puts(msg);
|
||||
};
|
||||
log(vertex_program.getInfoLog());
|
||||
log(vertex_program.getInfoDebugLog());
|
||||
log(fragment_program.getInfoLog());
|
||||
log(fragment_program.getInfoDebugLog());
|
||||
|
||||
std::vector<uint32_t> vertex_spirv;
|
||||
std::vector<uint32_t> fragment_spirv;
|
||||
|
||||
glslang::GlslangToSpv(*vertex_program.getIntermediate(EShLangVertex), vertex_spirv);
|
||||
glslang::GlslangToSpv(*fragment_program.getIntermediate(EShLangFragment), fragment_spirv);
|
||||
|
||||
vk::ShaderModuleCreateInfo smci;
|
||||
smci.setCode(vertex_spirv);
|
||||
vertex_module = device.createShaderModule(smci).value;
|
||||
smci.setCode(fragment_spirv);
|
||||
fragment_module = device.createShaderModule(smci).value;
|
||||
}
|
||||
|
||||
void vk2d::create_pipeline()
|
||||
{
|
||||
create_shader_modules();
|
||||
|
||||
if (!vertex_module || !fragment_module)
|
||||
assert(0);
|
||||
|
||||
vk::PipelineShaderStageCreateInfo vertex_ci;
|
||||
vertex_ci
|
||||
.setStage(vk::ShaderStageFlagBits::eVertex)
|
||||
.setModule(vertex_module)
|
||||
.setPName("main");
|
||||
|
||||
vk::PipelineShaderStageCreateInfo fragment_ci;
|
||||
fragment_ci
|
||||
.setStage(vk::ShaderStageFlagBits::eFragment)
|
||||
.setModule(fragment_module)
|
||||
.setPName("main");
|
||||
|
||||
std::vector<vk::PipelineShaderStageCreateInfo> stages = { vertex_ci, fragment_ci };
|
||||
|
||||
vk::PipelineVertexInputStateCreateInfo vertex_input_info;
|
||||
vertex_input_info
|
||||
.setVertexBindingDescriptionCount(0)
|
||||
.setVertexAttributeDescriptionCount(0);
|
||||
|
||||
// Add Vertex attributes here
|
||||
|
||||
vk::PipelineInputAssemblyStateCreateInfo pipeline_input_assembly_info;
|
||||
pipeline_input_assembly_info
|
||||
.setTopology(vk::PrimitiveTopology::eTriangleList)
|
||||
.setPrimitiveRestartEnable(false);
|
||||
|
||||
std::vector<vk::Viewport> viewports(1);
|
||||
viewports[0]
|
||||
.setX(0.0f)
|
||||
.setY(0.0f)
|
||||
.setWidth(swapchain.extents.width)
|
||||
.setHeight(swapchain.extents.height)
|
||||
.setMinDepth(0.0f)
|
||||
.setMaxDepth(1.0f);
|
||||
std::vector<vk::Rect2D> scissors(1);
|
||||
scissors[0].extent = swapchain.extents;
|
||||
scissors[0].offset = vk::Offset2D(0, 0);
|
||||
|
||||
vk::PipelineViewportStateCreateInfo pipeline_viewport_info;
|
||||
pipeline_viewport_info
|
||||
.setViewports(viewports)
|
||||
.setScissors(scissors);
|
||||
|
||||
vk::PipelineRasterizationStateCreateInfo rasterizer_info;
|
||||
rasterizer_info
|
||||
.setCullMode(vk::CullModeFlagBits::eBack)
|
||||
.setFrontFace(vk::FrontFace::eClockwise)
|
||||
.setLineWidth(1.0f)
|
||||
.setDepthClampEnable(false)
|
||||
.setRasterizerDiscardEnable(false)
|
||||
.setPolygonMode(vk::PolygonMode::eFill)
|
||||
.setDepthBiasEnable(false)
|
||||
.setRasterizerDiscardEnable(false);
|
||||
|
||||
vk::PipelineMultisampleStateCreateInfo multisample_info;
|
||||
multisample_info
|
||||
.setSampleShadingEnable(false)
|
||||
.setRasterizationSamples(vk::SampleCountFlagBits::e1);
|
||||
|
||||
vk::PipelineDepthStencilStateCreateInfo depth_stencil_info;
|
||||
depth_stencil_info.setDepthTestEnable(false);
|
||||
|
||||
vk::PipelineColorBlendAttachmentState blend_attachment_info;
|
||||
blend_attachment_info
|
||||
.setColorWriteMask(vk::ColorComponentFlagBits::eB |
|
||||
vk::ColorComponentFlagBits::eG |
|
||||
vk::ColorComponentFlagBits::eR |
|
||||
vk::ColorComponentFlagBits::eA)
|
||||
.setBlendEnable(true)
|
||||
.setColorBlendOp(vk::BlendOp::eAdd)
|
||||
.setSrcColorBlendFactor(vk::BlendFactor::eSrcAlpha)
|
||||
.setDstColorBlendFactor(vk::BlendFactor::eOneMinusSrcAlpha)
|
||||
.setAlphaBlendOp(vk::BlendOp::eAdd)
|
||||
.setSrcAlphaBlendFactor(vk::BlendFactor::eOne)
|
||||
.setSrcAlphaBlendFactor(vk::BlendFactor::eZero);
|
||||
|
||||
vk::PipelineColorBlendStateCreateInfo blend_state_info;
|
||||
blend_state_info
|
||||
.setLogicOpEnable(false)
|
||||
.setAttachmentCount(1)
|
||||
.setPAttachments(&blend_attachment_info);
|
||||
|
||||
vk::PipelineDynamicStateCreateInfo dynamic_state_info;
|
||||
dynamic_state_info.setDynamicStateCount(0);
|
||||
|
||||
vk::PipelineLayoutCreateInfo pipeline_layout_info;
|
||||
pipeline_layout_info
|
||||
.setSetLayoutCount(0)
|
||||
.setPushConstantRangeCount(0);
|
||||
|
||||
pipeline_layout = device.createPipelineLayout(pipeline_layout_info).value;
|
||||
|
||||
vk::GraphicsPipelineCreateInfo pipeline_create_info;
|
||||
pipeline_create_info
|
||||
.setStageCount(2)
|
||||
.setStages(stages)
|
||||
.setPVertexInputState(&vertex_input_info)
|
||||
.setPInputAssemblyState(&pipeline_input_assembly_info)
|
||||
.setPViewportState(&pipeline_viewport_info)
|
||||
.setPRasterizationState(&rasterizer_info)
|
||||
.setPMultisampleState(&multisample_info)
|
||||
.setPDepthStencilState(&depth_stencil_info)
|
||||
.setPColorBlendState(&blend_state_info)
|
||||
.setPDynamicState(&dynamic_state_info)
|
||||
.setLayout(pipeline_layout)
|
||||
.setRenderPass(render_pass)
|
||||
.setSubpass(0);
|
||||
|
||||
vk::ResultValue<vk::Pipeline> result = device.createGraphicsPipeline(nullptr, pipeline_create_info);
|
||||
graphics_pipeline = result.value;
|
||||
|
||||
device.destroyShaderModule(vertex_module);
|
||||
device.destroyShaderModule(fragment_module);
|
||||
}
|
||||
|
||||
void vk2d::create_render_pass()
|
||||
{
|
||||
vk::AttachmentDescription attachment_description(
|
||||
{},
|
||||
vk::Format::eB8G8R8A8Unorm,
|
||||
vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear,
|
||||
vk::AttachmentStoreOp::eStore,
|
||||
vk::AttachmentLoadOp::eLoad,
|
||||
vk::AttachmentStoreOp::eStore,
|
||||
vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::ePresentSrcKHR);
|
||||
|
||||
vk::AttachmentReference attachment_reference(0, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
attachment_reference
|
||||
.setAttachment(0)
|
||||
.setLayout(vk::ImageLayout::eColorAttachmentOptimal);
|
||||
|
||||
vk::SubpassDependency subpass_dependency;
|
||||
subpass_dependency
|
||||
.setSrcSubpass(VK_SUBPASS_EXTERNAL)
|
||||
.setDstSubpass(0)
|
||||
.setSrcStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
|
||||
.setSrcAccessMask(vk::AccessFlagBits(0))
|
||||
.setDstStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
|
||||
.setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite);
|
||||
|
||||
vk::SubpassDescription subpass_description;
|
||||
subpass_description
|
||||
.setPipelineBindPoint(vk::PipelineBindPoint::eGraphics)
|
||||
.setColorAttachments(attachment_reference);
|
||||
|
||||
vk::RenderPassCreateInfo render_pass_info(
|
||||
{},
|
||||
attachment_description,
|
||||
subpass_description,
|
||||
subpass_dependency);
|
||||
render_pass = device.createRenderPass(render_pass_info).value;
|
||||
}
|
||||
|
||||
void vk2d::create_framebuffers()
|
||||
{
|
||||
swapchain.framebuffers.resize(swapchain.images.size());
|
||||
|
||||
for (size_t i = 0; i < swapchain.images.size(); i++)
|
||||
{
|
||||
vk::FramebufferCreateInfo fci;
|
||||
std::vector<vk::ImageView> attachments = { swapchain.views[i].get() };
|
||||
fci
|
||||
.setAttachments(attachments)
|
||||
.setLayers(1)
|
||||
.setRenderPass(render_pass)
|
||||
.setWidth(swapchain.extents.width)
|
||||
.setHeight(swapchain.extents.height);
|
||||
|
||||
swapchain.framebuffers[i] = device.createFramebufferUnique(fci).value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void vk2d::create_command_buffers()
|
||||
{
|
||||
vk::CommandBufferAllocateInfo allocate_info;
|
||||
allocate_info.setCommandBufferCount(swapchain.images.size());
|
||||
allocate_info.setCommandPool(command_pool);
|
||||
swapchain.command_buffers = device.allocateCommandBuffers(allocate_info).value;
|
||||
|
||||
for (size_t i = 0; i < swapchain.command_buffers.size(); i++)
|
||||
{
|
||||
auto &cb = swapchain.command_buffers[i];
|
||||
|
||||
VK_CHECK(cb.begin(vk::CommandBufferBeginInfo{}));
|
||||
|
||||
vk::ClearColorValue color;
|
||||
color.setFloat32({ 0.0f, 1.0f, 1.0f, 0.0f});
|
||||
vk::ClearValue clear_value(color);
|
||||
vk::RenderPassBeginInfo rpbi;
|
||||
rpbi.setRenderPass(render_pass);
|
||||
rpbi.setFramebuffer(swapchain.framebuffers[i].get());
|
||||
rpbi.setPClearValues(&clear_value);
|
||||
rpbi.setClearValueCount(1);
|
||||
rpbi.renderArea.setExtent(swapchain.extents);
|
||||
rpbi.renderArea.setOffset({0, 0});
|
||||
cb.beginRenderPass(rpbi, vk::SubpassContents::eInline);
|
||||
cb.bindPipeline(vk::PipelineBindPoint::eGraphics, graphics_pipeline);
|
||||
cb.draw(3, 1, 0, 0);
|
||||
cb.endRenderPass();
|
||||
VK_CHECK(cb.end());
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "vulkan/vulkan.hpp"
|
||||
#include <fstream>
|
||||
|
||||
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
||||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
||||
#include <wayland-client.h>
|
||||
#endif
|
||||
|
||||
#define VK_CHECK(result) vk_check_result_function(result, __FILE__, __LINE__)
|
||||
inline void vk_check_result_function(vk::Result result, const char *file, int line)
|
||||
{
|
||||
if (result != vk::Result::eSuccess)
|
||||
{
|
||||
printf("%s:%d Vulkan error: %s\n", file, line, vk::to_string(result).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
class vk2d
|
||||
{
|
||||
public:
|
||||
vk2d();
|
||||
~vk2d();
|
||||
|
||||
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
||||
bool init_xlib_instance();
|
||||
void attach(Display *dpy, Window xid);
|
||||
#endif
|
||||
|
||||
bool init_device();
|
||||
void deinit();
|
||||
bool create_instance();
|
||||
void choose_physical_device();
|
||||
void create_device();
|
||||
void create_swapchain();
|
||||
void create_sync_objects();
|
||||
void create_shader_modules();
|
||||
void create_pipeline();
|
||||
void create_render_pass();
|
||||
void create_framebuffers();
|
||||
void create_command_buffers();
|
||||
void recreate_swapchain();
|
||||
void destroy_swapchain();
|
||||
void wait_idle();
|
||||
void draw();
|
||||
|
||||
vk::Instance instance;
|
||||
vk::PhysicalDevice physical_device;
|
||||
vk::SurfaceKHR surface;
|
||||
vk::Pipeline graphics_pipeline;
|
||||
vk::PipelineLayout pipeline_layout;
|
||||
vk::RenderPass render_pass;
|
||||
|
||||
vk::ShaderModule vertex_module;
|
||||
vk::ShaderModule fragment_module;
|
||||
|
||||
vk::Device device;
|
||||
vk::Queue queue;
|
||||
vk::CommandPool command_pool;
|
||||
size_t graphics_queue_index;
|
||||
|
||||
struct frame_t {
|
||||
vk::UniqueSemaphore render_finished;
|
||||
vk::UniqueSemaphore image_ready;
|
||||
vk::UniqueFence fence;
|
||||
};
|
||||
|
||||
static const size_t frame_queue_size = 3;
|
||||
size_t frame_queue_index;
|
||||
std::vector<frame_t> frame_queue;
|
||||
|
||||
struct {
|
||||
vk::SwapchainKHR obj;
|
||||
vk::Extent2D extents;
|
||||
std::vector<vk::Image> images;
|
||||
std::vector<vk::UniqueImageView> views;
|
||||
std::vector<vk::UniqueFramebuffer> framebuffers;
|
||||
std::vector<vk::CommandBuffer> command_buffers;
|
||||
std::vector<int> frame_fence;
|
||||
size_t size;
|
||||
} swapchain;
|
||||
|
||||
static bool dispatcher_initialized;
|
||||
};
|
|
@ -202,7 +202,7 @@ void PipelineImage::clear(vk::CommandBuffer cmd)
|
|||
vk::PipelineStageFlagBits::eFragmentShader,
|
||||
{}, {}, {},
|
||||
image_memory_barrier);
|
||||
|
||||
|
||||
current_layout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
||||
}
|
||||
|
||||
|
|
|
@ -330,7 +330,7 @@ void ShaderChain::update_descriptor_set(vk::CommandBuffer cmd, int pipe_num, int
|
|||
{
|
||||
assert(sampler.specifier < (int)pipelines.size());
|
||||
assert(sampler.specifier >= 0);
|
||||
|
||||
|
||||
if (!pipelines[sampler.specifier]->frame[last_frame_index].image.image)
|
||||
update_framebuffers(cmd, last_frame_index);
|
||||
auto &feedback_frame = pipelines[sampler.specifier]->frame[last_frame_index];
|
||||
|
|
|
@ -29,7 +29,7 @@ class SlangPipeline
|
|||
vk::UniqueSemaphore semaphore;
|
||||
vk::UniqueSampler sampler;
|
||||
|
||||
struct
|
||||
struct
|
||||
{
|
||||
vk::UniqueDescriptorSet descriptor_set;
|
||||
PipelineImage image;
|
||||
|
|
|
@ -140,7 +140,7 @@ void Texture::from_buffer(vk::CommandBuffer cmd,
|
|||
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
|
||||
.setDstAccessMask(vk::AccessFlagBits::eTransferRead)
|
||||
.setSubresourceRange(srr(base_level));
|
||||
|
||||
|
||||
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::PipelineStageFlagBits::eTransfer,
|
||||
{}, {}, {}, barrier);
|
||||
|
@ -285,7 +285,7 @@ void Texture::create(int width, int height, vk::Format fmt, vk::SamplerAddressMo
|
|||
sampler_create_info
|
||||
.setMagFilter(vk::Filter::eLinear)
|
||||
.setMinFilter(vk::Filter::eLinear);
|
||||
|
||||
|
||||
if (mipmap)
|
||||
sampler_create_info
|
||||
.setMinLod(0.0f)
|
||||
|
|
Loading…
Reference in New Issue