Cross-platform windowing and graphics (except for web)
This commit is contained in:
parent
e85a2facef
commit
6de280f12d
|
@ -1,3 +1,4 @@
|
||||||
.vscode/
|
.vscode/
|
||||||
build/
|
build/
|
||||||
ols.json
|
ols.json
|
||||||
|
log.txt
|
|
@ -3,7 +3,7 @@ Powervessel is a batteries-included framework made for real-time applications,
|
||||||
particularly games. It provides:
|
particularly games. It provides:
|
||||||
|
|
||||||
- [x] Seamless hot-reloading of user code
|
- [x] Seamless hot-reloading of user code
|
||||||
- [ ] Cross-platform windowing and graphics via [GLFW](https://glfw.org)
|
- [x] Cross-platform windowing and graphics via [GLFW](https://glfw.org)
|
||||||
- [ ] An Extensible WebGPU Renderer that supports both 2D and 3D
|
- [ ] An Extensible WebGPU Renderer that supports both 2D and 3D
|
||||||
- [ ] A Uniform Keyboard + Gamepad Input System
|
- [ ] A Uniform Keyboard + Gamepad Input System
|
||||||
- [ ] A Custom Audio System via [MiniAudio](https://miniaud.io)
|
- [ ] A Custom Audio System via [MiniAudio](https://miniaud.io)
|
||||||
|
|
13
app.odin
13
app.odin
|
@ -1,5 +1,7 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
|
import "window"
|
||||||
|
|
||||||
// NOTE: Restart means it's like the program's *just* been opened. Typically happens when memory layout changes
|
// NOTE: Restart means it's like the program's *just* been opened. Typically happens when memory layout changes
|
||||||
// Reload means that the code has changed, but state is still the same. More seamless and you're in the same place in your program.
|
// Reload means that the code has changed, but state is still the same. More seamless and you're in the same place in your program.
|
||||||
|
|
||||||
|
@ -10,9 +12,18 @@ App_Memory :: struct {
|
||||||
g_mem: ^App_Memory
|
g_mem: ^App_Memory
|
||||||
|
|
||||||
// Called upon first run OR full restart. Use it to set starting values (i.e. Player position, menu state)
|
// Called upon first run OR full restart. Use it to set starting values (i.e. Player position, menu state)
|
||||||
|
// Return value is window settings
|
||||||
@(export)
|
@(export)
|
||||||
app_init :: proc() {
|
app_init :: proc() -> window.Config {
|
||||||
g_mem = new(App_Memory)
|
g_mem = new(App_Memory)
|
||||||
|
|
||||||
|
return {
|
||||||
|
title = "Powervessel Template",
|
||||||
|
width = 1280,
|
||||||
|
height = 720,
|
||||||
|
resizable = true,
|
||||||
|
clear_color = {1.0, 0.0, 0.0, 1.0}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Typical update loop
|
// Typical update loop
|
||||||
|
|
|
@ -5,6 +5,7 @@ import "core:os"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
import app ".."
|
import app ".."
|
||||||
|
import "../window"
|
||||||
|
|
||||||
USE_TRACKING_ALLOCATOR :: ODIN_DEBUG
|
USE_TRACKING_ALLOCATOR :: ODIN_DEBUG
|
||||||
|
|
||||||
|
@ -31,12 +32,17 @@ main :: proc() {
|
||||||
logger := logh_err == os.ERROR_NONE ? log.create_file_logger(logh) : log.create_console_logger()
|
logger := logh_err == os.ERROR_NONE ? log.create_file_logger(logh) : log.create_console_logger()
|
||||||
context.logger = logger
|
context.logger = logger
|
||||||
|
|
||||||
// TODO Create Windoww
|
win_config := app.app_init()
|
||||||
app.app_init()
|
|
||||||
|
win_ok := window.init(win_config)
|
||||||
|
if !win_ok {
|
||||||
|
log.fatalf("Failed to init Window")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
window_open := true
|
window_open := true
|
||||||
for window_open {
|
for window_open {
|
||||||
window_open = app.app_update()
|
window_open = app.app_update() && window.update()
|
||||||
|
|
||||||
when USE_TRACKING_ALLOCATOR {
|
when USE_TRACKING_ALLOCATOR {
|
||||||
for b in tracking_allocator.bad_free_array {
|
for b in tracking_allocator.bad_free_array {
|
||||||
|
@ -51,7 +57,7 @@ main :: proc() {
|
||||||
|
|
||||||
free_all(context.temp_allocator)
|
free_all(context.temp_allocator)
|
||||||
app.app_shutdown()
|
app.app_shutdown()
|
||||||
// TODO Shutdown Window
|
window.shutdown()
|
||||||
|
|
||||||
if logh_err == os.ERROR_NONE {
|
if logh_err == os.ERROR_NONE {
|
||||||
log.destroy_file_logger(logger)
|
log.destroy_file_logger(logger)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import "core:fmt"
|
||||||
import "core:log"
|
import "core:log"
|
||||||
import "core:c/libc"
|
import "core:c/libc"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
import "../window"
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
DLL_EXT :: ".dll"
|
DLL_EXT :: ".dll"
|
||||||
|
@ -23,7 +24,7 @@ when ODIN_OS == .Windows {
|
||||||
}
|
}
|
||||||
|
|
||||||
Api :: struct {
|
Api :: struct {
|
||||||
init: proc(),
|
init: proc() -> window.Config,
|
||||||
update: proc() -> bool,
|
update: proc() -> bool,
|
||||||
shutdown: proc(),
|
shutdown: proc(),
|
||||||
memory: proc() -> (rawptr, int),
|
memory: proc() -> (rawptr, int),
|
||||||
|
@ -106,11 +107,16 @@ main :: proc() {
|
||||||
|
|
||||||
api_version += 1
|
api_version += 1
|
||||||
|
|
||||||
// TODO Create Window
|
win_config := api.init()
|
||||||
api.init()
|
|
||||||
|
win_ok := window.init(win_config)
|
||||||
|
if !win_ok {
|
||||||
|
log.fatalf("Failed to init Window")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if api.update() == false {
|
if api.update() == false || window.update() == false {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,8 +138,15 @@ main :: proc() {
|
||||||
api.shutdown()
|
api.shutdown()
|
||||||
reset_tracking_allocator(&tracking_allocator)
|
reset_tracking_allocator(&tracking_allocator)
|
||||||
api_unload(api)
|
api_unload(api)
|
||||||
|
window.shutdown()
|
||||||
|
|
||||||
api = new_api
|
api = new_api
|
||||||
api.init()
|
win_config = api.init()
|
||||||
|
win_ok = window.init(win_config)
|
||||||
|
if !win_ok {
|
||||||
|
log.fatalf("Failed to init Window")
|
||||||
|
return
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
api_unload(api)
|
api_unload(api)
|
||||||
api = new_api
|
api = new_api
|
||||||
|
@ -162,7 +175,7 @@ main :: proc() {
|
||||||
libc.getchar()
|
libc.getchar()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Shutdown Window
|
window.shutdown()
|
||||||
api_unload(api)
|
api_unload(api)
|
||||||
mem.tracking_allocator_destroy(&tracking_allocator)
|
mem.tracking_allocator_destroy(&tracking_allocator)
|
||||||
}
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
odin build releaser -out:build/app.exe
|
odin build releaser -out:build/app.exe -subsystem:windows
|
|
@ -0,0 +1,335 @@
|
||||||
|
package window
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:log"
|
||||||
|
import "core:time"
|
||||||
|
import "vendor:glfw"
|
||||||
|
import "vendor:wgpu"
|
||||||
|
import "vendor:wgpu/glfwglue"
|
||||||
|
|
||||||
|
Config :: struct {
|
||||||
|
title: string,
|
||||||
|
width, height: int,
|
||||||
|
resizable: bool,
|
||||||
|
clear_color: wgpu.Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
g_window: glfw.WindowHandle
|
||||||
|
g_wgpu_surface: wgpu.Surface
|
||||||
|
g_wgpu_queue: wgpu.Queue
|
||||||
|
g_wgpu_device: wgpu.Device
|
||||||
|
g_clear_color: wgpu.Color
|
||||||
|
|
||||||
|
// CURRENT LIMITATION: Only one window can be created per application
|
||||||
|
init :: proc(win_config: Config) -> bool {
|
||||||
|
using win_config
|
||||||
|
|
||||||
|
title_cstr, err := strings.clone_to_cstring(title, context.temp_allocator)
|
||||||
|
if err != nil {
|
||||||
|
log.errorf("Failed to translate title string to cstring")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if glfw.Init() == false {
|
||||||
|
log.errorf("Failed to init GLFW")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
glfw.WindowHint(glfw.CLIENT_API, glfw.NO_API)
|
||||||
|
if !resizable {
|
||||||
|
glfw.WindowHint(glfw.RESIZABLE, glfw.FALSE)
|
||||||
|
}
|
||||||
|
|
||||||
|
g_window = glfw.CreateWindow(i32(width), i32(height), title_cstr, nil, nil)
|
||||||
|
if g_window == nil {
|
||||||
|
log.errorf("Failed to create GLFW window")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
inst_desc := wgpu.InstanceDescriptor{
|
||||||
|
nextInChain = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
when ODIN_ARCH == .wasm32 {
|
||||||
|
instance := wgpu.CreateInstance(nil)
|
||||||
|
} else {
|
||||||
|
instance := wgpu.CreateInstance(&inst_desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if instance == nil {
|
||||||
|
log.errorf("Failed to initialize WebGPU instance")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
log.debugf("WebGPU Instance: {0}", instance)
|
||||||
|
|
||||||
|
g_wgpu_surface = glfwglue.GetSurface(instance, g_window)
|
||||||
|
|
||||||
|
log.debugf("Requesting WebGPU Adapter...")
|
||||||
|
|
||||||
|
adapter_opts: wgpu.RequestAdapterOptions
|
||||||
|
adapter_opts.nextInChain = nil
|
||||||
|
adapter_opts.compatibleSurface = g_wgpu_surface
|
||||||
|
adapter := request_adapter_sync(instance, &adapter_opts)
|
||||||
|
|
||||||
|
log.debugf("Got WebGPU Adapter: {0}", adapter)
|
||||||
|
when ODIN_DEBUG {
|
||||||
|
inspect_adapter(adapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debugf("Requesting WebGPU device...")
|
||||||
|
device_desc := wgpu.DeviceDescriptor{
|
||||||
|
nextInChain = nil,
|
||||||
|
label = "Powervessel Engine WebGPU Device",
|
||||||
|
requiredFeatureCount = 0,
|
||||||
|
requiredLimits = nil,
|
||||||
|
defaultQueue = {
|
||||||
|
nextInChain = nil,
|
||||||
|
label = "Powervessel Engine Default Queue"
|
||||||
|
},
|
||||||
|
deviceLostCallback = proc "cdecl" (reason: wgpu.DeviceLostReason, msg: cstring, raw_user_data: rawptr) {
|
||||||
|
context = runtime.default_context()
|
||||||
|
log.debugf("Device lost: reason {0}", reason)
|
||||||
|
if msg != "" do log.debugf(" ({0})", msg)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
g_wgpu_device = request_device_sync(adapter, &device_desc)
|
||||||
|
log.debugf("Got WebGPU device: {0}", g_wgpu_device)
|
||||||
|
|
||||||
|
on_device_error :: proc "cdecl" (type: wgpu.ErrorType, msg: cstring, raw_user_data: rawptr) {
|
||||||
|
context = runtime.default_context()
|
||||||
|
log.debugf("Uncaptured device error: type {0}", type)
|
||||||
|
if msg != "" do log.debugf(" ({0})", msg)
|
||||||
|
}
|
||||||
|
wgpu.DeviceSetUncapturedErrorCallback(g_wgpu_device, on_device_error, nil)
|
||||||
|
|
||||||
|
when ODIN_DEBUG {
|
||||||
|
inspect_device(g_wgpu_device)
|
||||||
|
}
|
||||||
|
|
||||||
|
g_wgpu_queue = wgpu.DeviceGetQueue(g_wgpu_device)
|
||||||
|
wgpu.QueueOnSubmittedWorkDone(g_wgpu_queue,
|
||||||
|
proc "cdecl" (status: wgpu.QueueWorkDoneStatus, raw_user_data: rawptr) {
|
||||||
|
context = runtime.default_context()
|
||||||
|
log.debugf("Queued work finished with status: {0}", status)
|
||||||
|
})
|
||||||
|
|
||||||
|
surface_format := wgpu.SurfaceGetPreferredFormat(g_wgpu_surface, adapter)
|
||||||
|
surface_config := wgpu.SurfaceConfiguration{
|
||||||
|
nextInChain = nil,
|
||||||
|
width = u32(width),
|
||||||
|
height = u32(height),
|
||||||
|
format = surface_format,
|
||||||
|
viewFormatCount = 0,
|
||||||
|
viewFormats = nil,
|
||||||
|
usage = {.RenderAttachment},
|
||||||
|
device = g_wgpu_device,
|
||||||
|
presentMode = .Fifo,
|
||||||
|
alphaMode = .Auto,
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu.SurfaceConfigure(g_wgpu_surface, &surface_config)
|
||||||
|
|
||||||
|
wgpu.AdapterRelease(adapter)
|
||||||
|
|
||||||
|
g_clear_color = clear_color
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
update :: proc() -> bool {
|
||||||
|
if glfw.WindowShouldClose(g_window) do return false
|
||||||
|
glfw.PollEvents()
|
||||||
|
|
||||||
|
rtv := get_render_target_view()
|
||||||
|
if rtv == nil {
|
||||||
|
log.fatalf("Failed to get render target view")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder_desc := wgpu.CommandEncoderDescriptor{
|
||||||
|
nextInChain = nil,
|
||||||
|
label = "Powervessel Engine Command Encoder"
|
||||||
|
}
|
||||||
|
encoder := wgpu.DeviceCreateCommandEncoder(g_wgpu_device, &encoder_desc)
|
||||||
|
|
||||||
|
color_attachment := wgpu.RenderPassColorAttachment{
|
||||||
|
view = rtv,
|
||||||
|
resolveTarget = nil,
|
||||||
|
loadOp = .Clear,
|
||||||
|
storeOp = .Store,
|
||||||
|
clearValue = g_clear_color
|
||||||
|
}
|
||||||
|
render_pass_desc := wgpu.RenderPassDescriptor{
|
||||||
|
nextInChain = nil,
|
||||||
|
colorAttachmentCount = 1,
|
||||||
|
colorAttachments = &color_attachment,
|
||||||
|
depthStencilAttachment = nil,
|
||||||
|
timestampWrites = nil,
|
||||||
|
}
|
||||||
|
render_pass := wgpu.CommandEncoderBeginRenderPass(encoder, &render_pass_desc)
|
||||||
|
|
||||||
|
wgpu.RenderPassEncoderEnd(render_pass)
|
||||||
|
wgpu.RenderPassEncoderRelease(render_pass)
|
||||||
|
|
||||||
|
buffer_desc := wgpu.CommandBufferDescriptor{
|
||||||
|
nextInChain = nil,
|
||||||
|
label = "Powervessel Engine Command Buffer"
|
||||||
|
}
|
||||||
|
buffer := wgpu.CommandEncoderFinish(encoder, &buffer_desc)
|
||||||
|
wgpu.CommandEncoderRelease(encoder)
|
||||||
|
|
||||||
|
wgpu.QueueSubmit(g_wgpu_queue, {buffer})
|
||||||
|
wgpu.CommandBufferRelease(buffer)
|
||||||
|
|
||||||
|
wgpu.TextureViewRelease(rtv)
|
||||||
|
when ODIN_ARCH != .wasm32 {
|
||||||
|
wgpu.SurfacePresent(g_wgpu_surface)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown :: proc() {
|
||||||
|
glfw.DestroyWindow(g_window)
|
||||||
|
glfw.Terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
request_adapter_sync :: proc(instance: wgpu.Instance, opts: ^wgpu.RequestAdapterOptions) -> wgpu.Adapter {
|
||||||
|
User_Data :: struct {
|
||||||
|
adapter: wgpu.Adapter,
|
||||||
|
request_ended: bool,
|
||||||
|
odin_ctx: runtime.Context
|
||||||
|
}
|
||||||
|
user_data: User_Data
|
||||||
|
user_data.odin_ctx = context
|
||||||
|
|
||||||
|
on_adapter_request_ended :: proc "cdecl" (status: wgpu.RequestAdapterStatus, adapter: wgpu.Adapter, msg: cstring, raw_user_data: rawptr) {
|
||||||
|
user_data := cast(^User_Data)(raw_user_data)
|
||||||
|
context = user_data.odin_ctx
|
||||||
|
|
||||||
|
if status == .Success {
|
||||||
|
user_data.adapter = adapter
|
||||||
|
} else {
|
||||||
|
log.errorf("Failed to get WebGPU adapter: {0}", msg)
|
||||||
|
}
|
||||||
|
user_data.request_ended = true
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu.InstanceRequestAdapter(instance, opts, on_adapter_request_ended, rawptr(&user_data))
|
||||||
|
|
||||||
|
for !user_data.request_ended {
|
||||||
|
time.sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(user_data.request_ended)
|
||||||
|
|
||||||
|
return user_data.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
request_device_sync :: proc(adapter: wgpu.Adapter, desc: ^wgpu.DeviceDescriptor) -> wgpu.Device {
|
||||||
|
User_Data :: struct {
|
||||||
|
device: wgpu.Device,
|
||||||
|
request_ended: bool,
|
||||||
|
odin_ctx: runtime.Context
|
||||||
|
}
|
||||||
|
user_data: User_Data
|
||||||
|
user_data.odin_ctx = context
|
||||||
|
|
||||||
|
on_device_request_end :: proc "cdecl" (status: wgpu.RequestDeviceStatus, device: wgpu.Device, msg: cstring, raw_user_data: rawptr) {
|
||||||
|
user_data := cast(^User_Data)(raw_user_data)
|
||||||
|
context = user_data.odin_ctx
|
||||||
|
|
||||||
|
if status == .Success {
|
||||||
|
user_data.device = device
|
||||||
|
} else {
|
||||||
|
log.errorf("Failed to get WebGPU device: {0}", msg)
|
||||||
|
}
|
||||||
|
user_data.request_ended = true
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu.AdapterRequestDevice(adapter, desc, on_device_request_end, rawptr(&user_data))
|
||||||
|
|
||||||
|
for !user_data.request_ended {
|
||||||
|
time.sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(user_data.request_ended)
|
||||||
|
|
||||||
|
return user_data.device
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
inspect_adapter :: proc(adapter: wgpu.Adapter) {
|
||||||
|
when ODIN_ARCH != .wasm32 {
|
||||||
|
limits, limits_ok := wgpu.AdapterGetLimits(adapter)
|
||||||
|
if limits_ok {
|
||||||
|
log.debugf("Adapter limits:")
|
||||||
|
log.debugf(" - maxTextureDimension1D: {0}", limits.limits.maxTextureDimension1D)
|
||||||
|
log.debugf(" - maxTextureDimension2D: {0}", limits.limits.maxTextureDimension2D)
|
||||||
|
log.debugf(" - maxTextureDimension3D: {0}", limits.limits.maxTextureDimension3D)
|
||||||
|
log.debugf(" - maxTextureArrayLayers: {0}", limits.limits.maxTextureArrayLayers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
features := wgpu.AdapterEnumerateFeatures(adapter, context.temp_allocator)
|
||||||
|
log.debugf("WebGPU Adapter Features:")
|
||||||
|
for f in features {
|
||||||
|
log.debugf(" - {0}", f)
|
||||||
|
}
|
||||||
|
|
||||||
|
properties := wgpu.AdapterGetProperties(adapter)
|
||||||
|
log.debugf("WebGPU Adapter Properties:")
|
||||||
|
log.debugf(" - vendorID: {0}", properties.vendorID)
|
||||||
|
log.debugf(" - vendorName: {0}", properties.vendorName)
|
||||||
|
log.debugf(" - architecture: {0}", properties.architecture)
|
||||||
|
log.debugf(" - deviceID: {0}", properties.deviceID)
|
||||||
|
log.debugf(" - name: {0}", properties.name)
|
||||||
|
log.debugf(" - driverDescription: {0}", properties.driverDescription)
|
||||||
|
log.debugf(" - adapterType: {0}", properties.adapterType)
|
||||||
|
log.debugf(" - backendType: {0}", properties.backendType)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
inspect_device :: proc(device: wgpu.Device) {
|
||||||
|
features := wgpu.DeviceEnumerateFeatures(device, context.temp_allocator)
|
||||||
|
log.debugf("WebGPU Device Features:")
|
||||||
|
for f in features {
|
||||||
|
log.debugf(" - {0}", f)
|
||||||
|
}
|
||||||
|
|
||||||
|
limits, limits_ok := wgpu.DeviceGetLimits(device)
|
||||||
|
if limits_ok {
|
||||||
|
log.debugf("Device limits:")
|
||||||
|
log.debugf(" - maxTextureDimension1D: {0}", limits.limits.maxTextureDimension1D)
|
||||||
|
log.debugf(" - maxTextureDimension2D: {0}", limits.limits.maxTextureDimension2D)
|
||||||
|
log.debugf(" - maxTextureDimension3D: {0}", limits.limits.maxTextureDimension3D)
|
||||||
|
log.debugf(" - maxTextureArrayLayers: {0}", limits.limits.maxTextureArrayLayers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
get_render_target_view :: proc() -> wgpu.TextureView {
|
||||||
|
surface_tex := wgpu.SurfaceGetCurrentTexture(g_wgpu_surface)
|
||||||
|
if surface_tex.status != .Success {
|
||||||
|
log.errorf("Failed to get surface texture: {0}", surface_tex.status)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
view_desc := wgpu.TextureViewDescriptor{
|
||||||
|
nextInChain = nil,
|
||||||
|
label = "Powervessel Engine Surface Texture View",
|
||||||
|
format = wgpu.TextureGetFormat(surface_tex.texture),
|
||||||
|
dimension = ._2D,
|
||||||
|
baseMipLevel = 0,
|
||||||
|
mipLevelCount = 1,
|
||||||
|
baseArrayLayer = 0,
|
||||||
|
arrayLayerCount = 1,
|
||||||
|
aspect = .All,
|
||||||
|
}
|
||||||
|
|
||||||
|
return wgpu.TextureCreateView(surface_tex.texture, &view_desc)
|
||||||
|
}
|
Loading…
Reference in New Issue