-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathrenderframe.go
214 lines (184 loc) · 6.19 KB
/
renderframe.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// Copyright (c) 2022, Cogent Core. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vgpu
import (
"image"
vk "github.com/goki/vulkan"
)
// RenderFrame is an offscreen, non-window-backed rendering target,
// functioning like a Surface
type RenderFrame struct {
// pointer to gpu device, for convenience
GPU *GPU
// device for this surface -- each window surface has its own device, configured for that surface
Device Device
// the Render for this RenderFrame, typically from a System
Render *Render
// has the current image format and dimensions
Format ImageFormat
// number of frames to maintain in the swapchain -- e.g., 2 = double-buffering, 3 = triple-buffering -- initially set to a requested amount, and after Init reflects actual number
NFrames int
// Framebuffers representing the Image owned by the RenderFrame -- we iterate through these in rendering subsequent frames
Frames []*Framebuffer
// semaphore used internally for waiting on acquisition of next frame
ImageAcquired vk.Semaphore `display:"-"`
// semaphore that surface user can wait on, will be activated when image has been acquired in AcquireNextFrame method
RenderDone vk.Semaphore `display:"-"`
// fence for rendering command running
RenderFence vk.Fence `display:"-"`
// do we own the device?
OwnDevice bool
}
// NewRenderFrameOwnDevice returns a new renderframe initialized for given GPU,
// of given size.
// This version creates a new Graphics device -- for purely offscreen usage.
func NewRenderFrameOwnDevice(gp *GPU, size image.Point) *RenderFrame {
rf := &RenderFrame{}
rf.Defaults()
rf.Format.Size = size
rf.Init(gp, true) // make own device
return rf
}
// NewRenderFrame returns a new renderframe initialized for given GPU,
// of given size.
// using given device, e.g., from a Surface -- to transition images
// from renderframe to surface, they must use the same device.
// if device is nil, own device is created.
func NewRenderFrame(gp *GPU, dev *Device, size image.Point) *RenderFrame {
rf := &RenderFrame{}
rf.Defaults()
rf.Format.Size = size
if dev != nil {
rf.Device = *dev
rf.Init(gp, false)
} else {
rf.Init(gp, true) // make dev
}
return rf
}
func (rf *RenderFrame) Defaults() {
rf.NFrames = 1
rf.Format.Defaults()
// note: screen-correct results obtained by using Srgb here, which forces
// this format in the final output. Looks like what comes out from direct rendering.
rf.Format.Set(1024, 768, vk.FormatR8g8b8a8Srgb)
// rf.Format.Set(1024, 768, vk.FormatR8g8b8a8Unorm)
rf.Format.SetMultisample(4)
}
// Init initializes the device and all other resources for the renderframe.
func (rf *RenderFrame) Init(gp *GPU, makeDevice bool) error {
rf.GPU = gp
if makeDevice {
rf.OwnDevice = true
rf.Device.Init(gp, vk.QueueGraphicsBit)
} else {
rf.OwnDevice = false
}
rf.Config()
return nil
}
// Config configures the framebuffers etc
func (rf *RenderFrame) Config() {
dev := rf.Device.Device
rf.ImageAcquired = NewSemaphore(dev)
rf.RenderDone = NewSemaphore(dev)
rf.RenderFence = NewFence(dev)
rf.Frames = make([]*Framebuffer, rf.NFrames)
for i := 0; i < rf.NFrames; i++ {
fr := &Framebuffer{}
fr.ConfigRenderImage(rf.GPU, dev, rf.Format)
rf.Frames[i] = fr
}
}
// Free frees any existing (for ReInit or Destroy)
func (rf *RenderFrame) Free() {
dev := rf.Device.Device
vk.DeviceWaitIdle(dev)
vk.DestroySemaphore(dev, rf.ImageAcquired, nil)
vk.DestroySemaphore(dev, rf.RenderDone, nil)
vk.DestroyFence(dev, rf.RenderFence, nil)
for _, fr := range rf.Frames {
fr.Destroy()
}
rf.Frames = nil
}
// SetRender sets the Render and updates frames accordingly
func (rf *RenderFrame) SetRender(rp *Render) {
rf.Render = rp
for _, fr := range rf.Frames {
fr.ConfigRender(rp)
}
}
// SetSize sets the size for the render frame,
// doesn't do anything if already that size (returns fale)
func (rf *RenderFrame) SetSize(size image.Point) bool {
if rf.Format.Size == size {
return false
}
rf.Format.Size = size
rf.ReConfig()
return true
}
// ReConfig reconfigures rendering
func (rf *RenderFrame) ReConfig() {
rf.Render.SetSize(rf.Format.Size)
rf.ReConfigFrames()
}
// ReConfigFrames re-configures the Famebuffers
// using exiting settings.
// Assumes Config has been called.
func (rf *RenderFrame) ReConfigFrames() {
for _, fr := range rf.Frames {
fr.ConfigRenderImage(rf.GPU, rf.Device.Device, rf.Format)
fr.ConfigRender(rf.Render)
}
}
func (rf *RenderFrame) Destroy() {
rf.Free()
if rf.OwnDevice {
rf.Device.Destroy()
}
rf.GPU = nil
}
// SubmitRender submits a rendering command that must have been added
// to the given command buffer, calling CmdEnd on the buffer first.
// This buffer triggers the associated Fence logic to control the
// sequencing of render commands over time.
// The ImageAcquired semaphore before the command is run.
func (rf *RenderFrame) SubmitRender(cmd vk.CommandBuffer) {
dev := rf.Device.Device
vk.ResetFences(dev, 1, []vk.Fence{rf.RenderFence})
CmdEnd(cmd)
ret := vk.QueueSubmit(rf.Device.Queue, 1, []vk.SubmitInfo{{
SType: vk.StructureTypeSubmitInfo,
PWaitDstStageMask: []vk.PipelineStageFlags{
vk.PipelineStageFlags(vk.PipelineStageColorAttachmentOutputBit),
},
// WaitSemaphoreCount: 1,
// PWaitSemaphores: []vk.Semaphore{rf.ImageAcquired},
CommandBufferCount: 1,
PCommandBuffers: []vk.CommandBuffer{cmd},
// SignalSemaphoreCount: 1,
// PSignalSemaphores: []vk.Semaphore{rf.RenderDone},
}}, rf.RenderFence)
IfPanic(NewError(ret))
}
// WaitForRender waits until the last submitted render completes
func (rf *RenderFrame) WaitForRender() {
dev := rf.Device.Device
vk.WaitForFences(dev, 1, []vk.Fence{rf.RenderFence}, vk.True, vk.MaxUint64)
vk.ResetFences(dev, 1, []vk.Fence{rf.RenderFence})
}
// GrabImage grabs rendered image of given index to Framebuffer.ImageGrab.
// must have waited for render already.
func (rf *RenderFrame) GrabImage(cmd vk.CommandBuffer, idx int) {
dev := rf.Device.Device
rf.Frames[idx].GrabImage(dev, cmd)
}
// GrabDepthImage grabs rendered depth image from the Render,
// must have waited for render already.
func (rf *RenderFrame) GrabDepthImage(cmd vk.CommandBuffer) {
dev := rf.Device.Device
rf.Render.GrabDepthImage(dev, cmd)
}