Skip to content

Commit 2aaf3b3

Browse files
RolandGorzSpasi
authored andcommitted
feat(Vulkan) add hello_triangle sample
Ported from Vulkan-Samples: https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/api/hello_triangle
1 parent 7f45fc2 commit 2aaf3b3

File tree

7 files changed

+1805
-0
lines changed

7 files changed

+1805
-0
lines changed
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
/*
2+
* Copyright LWJGL. All rights reserved.
3+
* License terms: https://www.lwjgl.org/license
4+
*/
5+
package org.lwjgl.demo.vulkan.khronos;
6+
7+
import org.lwjgl.*;
8+
import org.lwjgl.system.*;
9+
import org.lwjgl.vulkan.*;
10+
11+
import java.io.*;
12+
import java.nio.*;
13+
import java.util.*;
14+
15+
import static org.lwjgl.demo.util.IOUtil.*;
16+
import static org.lwjgl.system.MemoryStack.*;
17+
import static org.lwjgl.util.shaderc.Shaderc.*;
18+
import static org.lwjgl.vulkan.KHRSurface.*;
19+
import static org.lwjgl.vulkan.VK10.*;
20+
21+
/** Base class and utilities for the hello_triangle samples. */
22+
public abstract class Demo implements AutoCloseable {
23+
24+
static final long UINT64_MAX = 0xFFFFFFFFFFFFFFFFL;
25+
26+
static final boolean VK_DEBUG;
27+
static {
28+
String property = System.getProperty("debug");
29+
VK_DEBUG = property == null || Boolean.parseBoolean(property);
30+
}
31+
32+
protected Demo() {
33+
}
34+
35+
abstract boolean prepare(Window window);
36+
abstract boolean resize();
37+
abstract void update();
38+
39+
static void VK_CHECK(int vkResult) {
40+
if (vkResult != VK_SUCCESS) {
41+
throw new RuntimeException("Detected Vulkan error: " + vkResult);
42+
}
43+
}
44+
45+
static void select_surface_format(VkPhysicalDevice gpu, long surface, VkSurfaceFormatKHR formatOut, int... preferred_formats) {
46+
if (preferred_formats.length == 0) {
47+
preferred_formats = new int[] {
48+
VK_FORMAT_R8G8B8A8_SRGB,
49+
VK_FORMAT_B8G8R8A8_SRGB,
50+
VK_FORMAT_A8B8G8R8_SRGB_PACK32
51+
};
52+
}
53+
54+
try (MemoryStack stack = MemoryStack.stackPush()) {
55+
IntBuffer pi = stack.mallocInt(1);
56+
57+
VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, pi, null));
58+
int surface_format_count = pi.get(0);
59+
assert (0 < surface_format_count);
60+
61+
try (VkSurfaceFormatKHR.Buffer supported_surface_formats = VkSurfaceFormatKHR.calloc(surface_format_count)) {
62+
VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, pi, supported_surface_formats));
63+
64+
// We use the first supported format as a fallback in case none of the preferred formats is available
65+
int it = 0;
66+
67+
out:
68+
for (int i = 0; i < surface_format_count; i++) {
69+
int supportedFormat = supported_surface_formats.get(i).format();
70+
for (int format : preferred_formats) {
71+
if (format == supportedFormat) {
72+
it = i;
73+
break out;
74+
}
75+
}
76+
}
77+
78+
supported_surface_formats.get(it, formatOut);
79+
}
80+
}
81+
}
82+
83+
static long compile_shader(String fileName, int shaderType, long compiler, long options) {
84+
ByteBuffer shader_source;
85+
try {
86+
shader_source = ioResourceToByteBuffer(fileName, 1024);
87+
} catch (IOException e) {
88+
throw new RuntimeException("Failed to load shader source", e);
89+
}
90+
91+
long result;
92+
try (MemoryStack stack = stackPush()) {
93+
result = shaderc_compile_into_spv(
94+
compiler,
95+
shader_source,
96+
shaderType,
97+
stack.UTF8(fileName),
98+
stack.UTF8("main"),
99+
options
100+
);
101+
}
102+
103+
if (shaderc_result_get_compilation_status(result) != shaderc_compilation_status_success) {
104+
throw new RuntimeException("Shader compilation failed: " + shaderc_result_get_error_message(result));
105+
}
106+
107+
return result;
108+
}
109+
110+
111+
public static class Instance {
112+
113+
private final VkInstance handle;
114+
115+
private final List<PhysicalDevice> gpus = new ArrayList<>();
116+
117+
Instance(VkInstance instance) {
118+
handle = instance;
119+
if (handle.address() != VK_NULL_HANDLE) {
120+
query_gpus();
121+
} else {
122+
throw new RuntimeException("Instance not valid");
123+
}
124+
}
125+
126+
void query_gpus() {
127+
try (MemoryStack stack = MemoryStack.stackPush()) {
128+
// Querying valid physical devices on the machine
129+
IntBuffer physical_device_count = stack.mallocInt(1);
130+
VK_CHECK(vkEnumeratePhysicalDevices(handle, physical_device_count, null));
131+
132+
if (physical_device_count.get(0) < 1) {
133+
throw new RuntimeException("Couldn't find a physical device that supports Vulkan.");
134+
}
135+
136+
PointerBuffer physical_devices = stack.mallocPointer(physical_device_count.get(0));
137+
VK_CHECK(vkEnumeratePhysicalDevices(handle, physical_device_count, physical_devices));
138+
139+
// Create gpus wrapper objects from the VkPhysicalDevice's
140+
for (int i = 0; i < physical_devices.capacity(); ++i) {
141+
gpus.add(new PhysicalDevice(new VkPhysicalDevice(physical_devices.get(i), handle)));
142+
}
143+
}
144+
}
145+
146+
public void free() {
147+
for (PhysicalDevice gpu : gpus) {
148+
gpu.free();
149+
}
150+
vkDestroyInstance(handle, null);
151+
}
152+
153+
public VkInstance getHandle() {
154+
return handle;
155+
}
156+
157+
}
158+
159+
static class PhysicalDevice {
160+
161+
private final VkPhysicalDevice handle;
162+
163+
private final VkPhysicalDeviceFeatures features = VkPhysicalDeviceFeatures.calloc();
164+
private final VkPhysicalDeviceProperties properties = VkPhysicalDeviceProperties.calloc();
165+
private final VkPhysicalDeviceMemoryProperties memory_properties = VkPhysicalDeviceMemoryProperties.calloc();
166+
167+
private final VkQueueFamilyProperties.Buffer queue_family_properties;
168+
private final VkExtensionProperties.Buffer device_extensions;
169+
170+
PhysicalDevice(VkPhysicalDevice physical_device) {
171+
handle = physical_device;
172+
173+
vkGetPhysicalDeviceFeatures(physical_device, features);
174+
vkGetPhysicalDeviceProperties(physical_device, properties);
175+
vkGetPhysicalDeviceMemoryProperties(physical_device, memory_properties);
176+
177+
System.out.printf("Found GPU: %s%n", properties.deviceNameString());
178+
try (MemoryStack stack = stackPush()) {
179+
IntBuffer queue_family_properties_count = stack.mallocInt(1);
180+
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, queue_family_properties_count, null);
181+
182+
queue_family_properties = VkQueueFamilyProperties.calloc(queue_family_properties_count.get(0));
183+
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, queue_family_properties_count, queue_family_properties);
184+
185+
IntBuffer device_extension_count = stack.mallocInt(1);
186+
VK_CHECK(vkEnumerateDeviceExtensionProperties(get_handle(), (ByteBuffer)null, device_extension_count, null));
187+
188+
device_extensions = VkExtensionProperties.calloc(device_extension_count.get(0));
189+
VK_CHECK(vkEnumerateDeviceExtensionProperties(get_handle(), (ByteBuffer)null, device_extension_count, device_extensions));
190+
}
191+
192+
// Display supported extensions
193+
if (device_extensions.capacity() > 0) {
194+
if (VK_DEBUG) {
195+
System.out.println("[debug] Device supports the following extensions:");
196+
}
197+
for (VkExtensionProperties extension : device_extensions) {
198+
if (VK_DEBUG) {
199+
System.out.printf("[debug] \t%s%n", extension.extensionNameString());
200+
}
201+
}
202+
}
203+
}
204+
205+
public VkPhysicalDevice get_handle() {
206+
return handle;
207+
}
208+
209+
public void free() {
210+
device_extensions.free();
211+
queue_family_properties.free();
212+
memory_properties.free();
213+
properties.free();
214+
features.free();
215+
}
216+
}
217+
218+
/**
219+
* Swapchain state
220+
*/
221+
static class SwapchainDimensions {
222+
// Width of the swapchain.
223+
int width;
224+
225+
// Height of the swapchain.
226+
int height;
227+
228+
// Pixel format of the swapchain.
229+
int format;
230+
231+
SwapchainDimensions(int width, int height, int format) {
232+
this.width = width;
233+
this.height = height;
234+
this.format = format;
235+
}
236+
}
237+
238+
/**
239+
* Per-frame data
240+
*/
241+
static class PerFrame {
242+
long queue_submit_fence = VK_NULL_HANDLE;
243+
long primary_command_pool = VK_NULL_HANDLE;
244+
245+
VkCommandBuffer primary_command_buffer;
246+
247+
long swapchain_acquire_semaphore = VK_NULL_HANDLE;
248+
long swapchain_release_semaphore = VK_NULL_HANDLE;
249+
}
250+
251+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright LWJGL. All rights reserved.
3+
* License terms: https://www.lwjgl.org/license
4+
*/
5+
package org.lwjgl.demo.vulkan.khronos;
6+
7+
import org.joml.*;
8+
import org.lwjgl.*;
9+
import org.lwjgl.glfw.*;
10+
import org.lwjgl.system.*;
11+
12+
import java.nio.*;
13+
import java.util.*;
14+
15+
import static org.lwjgl.glfw.GLFW.*;
16+
import static org.lwjgl.glfw.GLFWVulkan.*;
17+
import static org.lwjgl.system.MemoryStack.*;
18+
import static org.lwjgl.system.MemoryUtil.*;
19+
import static org.lwjgl.vulkan.VK10.*;
20+
21+
/** GLFW window implementation. */
22+
public class GLFWWindow implements Window {
23+
24+
private final long handle;
25+
26+
public GLFWWindow(int width, int height, String title) {
27+
// Set up an error callback. The default implementation
28+
// will print the error message in System.err.
29+
GLFWErrorCallback.createPrint(System.err).set();
30+
31+
// Initialize GLFW. Most GLFW functions will not work before doing this.
32+
if (!glfwInit()) {
33+
throw new IllegalStateException("Unable to initialize GLFW");
34+
}
35+
if (!glfwVulkanSupported()) {
36+
throw new RuntimeException("Cannot find a compatible Vulkan installable client driver (ICD)");
37+
}
38+
39+
// Configure GLFW
40+
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
41+
42+
// Create the window
43+
handle = glfwCreateWindow(width, height, title, NULL, NULL);
44+
45+
glfwSetKeyCallback(handle, (windowHnd, key, scancode, action, mods) -> {
46+
if (action != GLFW_RELEASE) {
47+
return;
48+
}
49+
50+
switch (key) {
51+
case GLFW_KEY_ESCAPE:
52+
glfwSetWindowShouldClose(windowHnd, true);
53+
break;
54+
}
55+
});
56+
}
57+
58+
@Override
59+
public PointerBuffer getRequiredExtensions() {
60+
PointerBuffer requiredExtensions = glfwGetRequiredInstanceExtensions();
61+
if (requiredExtensions == null) {
62+
throw new RuntimeException("glfwGetRequiredInstanceExtensions returned null");
63+
}
64+
return requiredExtensions;
65+
}
66+
67+
@Override
68+
public long create_surface(Demo.Instance instance) {
69+
if (instance == null) {
70+
return NULL;
71+
}
72+
try (MemoryStack stack = stackPush()) {
73+
LongBuffer pl = stack.mallocLong(1);
74+
75+
int result = glfwCreateWindowSurface(instance.getHandle(), handle, null, pl);
76+
if (result != VK_SUCCESS) {
77+
return NULL;
78+
}
79+
80+
return pl.get(0);
81+
}
82+
}
83+
84+
@Override
85+
public Vector2i getExtent() {
86+
try (MemoryStack stack = stackPush()) {
87+
IntBuffer pw = stack.mallocInt(1);
88+
IntBuffer ph = stack.mallocInt(1);
89+
90+
glfwGetWindowSize(handle, pw, ph);
91+
92+
return new Vector2i(pw.get(0), ph.get(0));
93+
}
94+
}
95+
96+
@Override
97+
public void mainLoop(Demo app) {
98+
glfwSetWindowSizeCallback(handle, GLFWWindowSizeCallback
99+
.create((window, w, h) -> app.resize()));
100+
101+
while (!glfwWindowShouldClose(handle)) {
102+
glfwPollEvents();
103+
app.update();
104+
}
105+
}
106+
107+
@Override
108+
public void close() {
109+
// Free the window callbacks and destroy the window
110+
Callbacks.glfwFreeCallbacks(handle);
111+
glfwDestroyWindow(handle);
112+
113+
// Terminate GLFW and free the error callback
114+
glfwTerminate();
115+
Objects.requireNonNull(glfwSetErrorCallback(null)).free();
116+
}
117+
}

0 commit comments

Comments
 (0)