1 /*
2 * Copyright © Microsoft Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "d3d12_wgl_public.h"
25
26 #include <new>
27
28 #include <windows.h>
29 #include <wrl.h>
30
31 #include "util/u_memory.h"
32 #include "util/u_inlines.h"
33 #include "frontend/api.h"
34 #include "frontend/winsys_handle.h"
35
36 #include "stw_device.h"
37 #include "stw_pixelformat.h"
38 #include "stw_winsys.h"
39
40 #include "d3d12/d3d12_format.h"
41 #include "d3d12/d3d12_resource.h"
42 #include "d3d12/d3d12_screen.h"
43
44 constexpr uint32_t num_buffers = 2;
45 static int current_backbuffer_index = 0;
46 static bool has_signaled_first_time = false;
47 static int cached_interval = 1;
48
49 struct d3d12_wgl_framebuffer {
50 struct stw_winsys_framebuffer base;
51
52 struct d3d12_screen *screen;
53 enum pipe_format pformat;
54 ID3D12Resource *images[num_buffers];
55 D3D12_CPU_DESCRIPTOR_HANDLE rtvs[num_buffers];
56 ID3D12DescriptorHeap *rtvHeap;
57 pipe_resource *buffers[num_buffers];
58 };
59
60 static struct d3d12_wgl_framebuffer*
d3d12_wgl_framebuffer(struct stw_winsys_framebuffer * fb)61 d3d12_wgl_framebuffer(struct stw_winsys_framebuffer *fb)
62 {
63 return (struct d3d12_wgl_framebuffer *) fb;
64 }
65
66 static void
d3d12_wgl_framebuffer_destroy(struct stw_winsys_framebuffer * fb,pipe_context * ctx)67 d3d12_wgl_framebuffer_destroy(struct stw_winsys_framebuffer *fb,
68 pipe_context *ctx)
69 {
70 struct d3d12_wgl_framebuffer *framebuffer = d3d12_wgl_framebuffer(fb);
71 struct pipe_fence_handle *fence = NULL;
72
73 if (ctx) {
74 /* Ensure all resources are flushed */
75 ctx->flush(ctx, &fence, PIPE_FLUSH_HINT_FINISH);
76 if (fence) {
77 ctx->screen->fence_finish(ctx->screen, ctx, fence, OS_TIMEOUT_INFINITE);
78 ctx->screen->fence_reference(ctx->screen, &fence, NULL);
79 }
80 }
81
82 framebuffer->rtvHeap->Release();
83 for (int i = 0; i < num_buffers; ++i) {
84 if (framebuffer->buffers[i]) {
85 d3d12_resource_release(d3d12_resource(framebuffer->buffers[i]));
86 pipe_resource_reference(&framebuffer->buffers[i], NULL);
87 }
88 }
89
90 delete framebuffer;
91 }
92
93 static void
d3d12_wgl_framebuffer_resize(stw_winsys_framebuffer * fb,pipe_context * ctx,pipe_resource * templ)94 d3d12_wgl_framebuffer_resize(stw_winsys_framebuffer *fb,
95 pipe_context *ctx,
96 pipe_resource *templ)
97 {
98 struct d3d12_wgl_framebuffer *framebuffer = d3d12_wgl_framebuffer(fb);
99
100 if (framebuffer->rtvHeap == NULL) {
101 D3D12_DESCRIPTOR_HEAP_DESC descHeapDesc = {};
102 descHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
103 descHeapDesc.NumDescriptors = num_buffers;
104 framebuffer->screen->dev->CreateDescriptorHeap(&descHeapDesc,
105 IID_PPV_ARGS(&framebuffer->rtvHeap));
106 }
107
108 // Release the old images
109 for (int i = 0; i < num_buffers; i++) {
110 if (framebuffer->buffers[i]) {
111 d3d12_resource_release(d3d12_resource(framebuffer->buffers[i]));
112 pipe_resource_reference(&framebuffer->buffers[i], NULL);
113 }
114 }
115
116 D3D12_HEAP_PROPERTIES heapProps = {};
117 heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
118
119 D3D12_RESOURCE_DESC resDesc;
120 resDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
121 resDesc.Width = templ->width0;
122 resDesc.Height = templ->height0;
123 resDesc.Alignment = 0;
124 resDesc.DepthOrArraySize = 1;
125 resDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
126 resDesc.Format = d3d12_get_format(templ->format);
127 resDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
128 resDesc.MipLevels = 1;
129 resDesc.SampleDesc.Count = 1;
130 resDesc.SampleDesc.Quality = 0;
131
132 D3D12_CLEAR_VALUE optimizedClearValue = {};
133 optimizedClearValue.Format = resDesc.Format;
134
135 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
136 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
137 rtvDesc.Format = resDesc.Format;
138 rtvDesc.Texture2D.MipSlice = 0;
139 rtvDesc.Texture2D.PlaneSlice = 0;
140
141 for (int i = 0; i < num_buffers; i++) {
142 if (FAILED(framebuffer->screen->dev->CreateCommittedResource(
143 &heapProps,
144 D3D12_HEAP_FLAG_ALLOW_DISPLAY,
145 &resDesc,
146 D3D12_RESOURCE_STATE_PRESENT,
147 &optimizedClearValue,
148 IID_PPV_ARGS(&framebuffer->images[i])
149 ))) {
150 assert(0);
151 }
152
153 framebuffer->rtvs[i].ptr =
154 framebuffer->rtvHeap->GetCPUDescriptorHandleForHeapStart().ptr +
155 ((int64_t)i * framebuffer->screen->dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV));
156
157 framebuffer->screen->dev->CreateRenderTargetView(
158 framebuffer->images[i],
159 &rtvDesc,
160 framebuffer->rtvs[i]
161 );
162 }
163
164 framebuffer->pformat = templ->format;
165 }
166
167 static bool
d3d12_wgl_framebuffer_present(stw_winsys_framebuffer * fb,int interval)168 d3d12_wgl_framebuffer_present(stw_winsys_framebuffer *fb, int interval)
169 {
170 auto framebuffer = d3d12_wgl_framebuffer(fb);
171 D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParams = {};
172 planeParams.Token = framebuffer->screen->frame_token;
173 planeParams.ResourceCount = 1;
174 planeParams.ppResources = &framebuffer->images[current_backbuffer_index];
175
176 D3D12XBOX_PRESENT_PARAMETERS presentParams = {};
177 presentParams.Flags = (interval == 0) ?
178 D3D12XBOX_PRESENT_FLAG_IMMEDIATE :
179 D3D12XBOX_PRESENT_FLAG_NONE;
180
181 int clamped_interval = CLAMP(interval, 1, 4); // SetFrameIntervalX only supports values [1,4]
182 if (cached_interval != clamped_interval) {
183 framebuffer->screen->dev->SetFrameIntervalX(
184 nullptr,
185 D3D12XBOX_FRAME_INTERVAL_60_HZ,
186 clamped_interval,
187 D3D12XBOX_FRAME_INTERVAL_FLAG_NONE
188 );
189 framebuffer->screen->dev->ScheduleFrameEventX(
190 D3D12XBOX_FRAME_EVENT_ORIGIN,
191 0,
192 nullptr,
193 D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE
194 );
195 cached_interval = clamped_interval;
196 }
197
198 framebuffer->screen->cmdqueue->PresentX(1, &planeParams, &presentParams);
199
200 current_backbuffer_index = !current_backbuffer_index;
201
202 framebuffer->screen->frame_token = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL;
203 framebuffer->screen->dev->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE,
204 nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE,
205 &framebuffer->screen->frame_token);
206
207 return true;
208 }
209
210 static struct pipe_resource*
d3d12_wgl_framebuffer_get_resource(struct stw_winsys_framebuffer * pframebuffer,st_attachment_type statt)211 d3d12_wgl_framebuffer_get_resource(struct stw_winsys_framebuffer *pframebuffer,
212 st_attachment_type statt)
213 {
214 auto framebuffer = d3d12_wgl_framebuffer(pframebuffer);
215 auto pscreen = &framebuffer->screen->base;
216
217 UINT index = current_backbuffer_index;
218 if (statt == ST_ATTACHMENT_FRONT_LEFT)
219 index = !index;
220
221 if (framebuffer->buffers[index]) {
222 pipe_reference(NULL, &framebuffer->buffers[index]->reference);
223 return framebuffer->buffers[index];
224 }
225
226 ID3D12Resource *res = framebuffer->images[index];
227
228 struct winsys_handle handle;
229 memset(&handle, 0, sizeof(handle));
230 handle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
231 handle.format = framebuffer->pformat;
232 handle.com_obj = res;
233
234 D3D12_RESOURCE_DESC res_desc = GetDesc(res);
235
236 struct pipe_resource templ;
237 memset(&templ, 0, sizeof(templ));
238 templ.target = PIPE_TEXTURE_2D;
239 templ.format = framebuffer->pformat;
240 templ.width0 = res_desc.Width;
241 templ.height0 = res_desc.Height;
242 templ.depth0 = 1;
243 templ.array_size = res_desc.DepthOrArraySize;
244 templ.nr_samples = res_desc.SampleDesc.Count;
245 templ.last_level = res_desc.MipLevels - 1;
246 templ.bind = PIPE_BIND_DISPLAY_TARGET | PIPE_BIND_RENDER_TARGET;
247 templ.usage = PIPE_USAGE_DEFAULT;
248 templ.flags = 0;
249
250 pipe_resource_reference(&framebuffer->buffers[index],
251 pscreen->resource_from_handle(pscreen, &templ, &handle,
252 PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE));
253 return framebuffer->buffers[index];
254 }
255
256 struct stw_winsys_framebuffer*
d3d12_wgl_create_framebuffer(struct pipe_screen * screen,HWND hWnd,int iPixelFormat)257 d3d12_wgl_create_framebuffer(struct pipe_screen *screen,
258 HWND hWnd,
259 int iPixelFormat)
260 {
261 const struct stw_pixelformat_info *pfi =
262 stw_pixelformat_get_info(iPixelFormat);
263 if (!(pfi->pfd.dwFlags & PFD_DOUBLEBUFFER) ||
264 (pfi->pfd.dwFlags & PFD_SUPPORT_GDI))
265 return NULL;
266
267 struct d3d12_wgl_framebuffer *fb = CALLOC_STRUCT(d3d12_wgl_framebuffer);
268 if (!fb)
269 return NULL;
270
271 new (fb) struct d3d12_wgl_framebuffer();
272
273 fb->screen = d3d12_screen(screen);
274 fb->images[0] = NULL;
275 fb->images[1] = NULL;
276 fb->rtvHeap = NULL;
277 fb->base.destroy = d3d12_wgl_framebuffer_destroy;
278 fb->base.resize = d3d12_wgl_framebuffer_resize;
279 fb->base.present = d3d12_wgl_framebuffer_present;
280 fb->base.get_resource = d3d12_wgl_framebuffer_get_resource;
281
282 // Xbox applications must manually handle Suspend/Resume events on the Command Queue.
283 // To allow the application to access the queue, we store a pointer in the HWND's user data.
284 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) fb->screen->cmdqueue);
285
286 // Schedule the frame interval and origin frame event
287 fb->screen->dev->SetFrameIntervalX(
288 nullptr,
289 D3D12XBOX_FRAME_INTERVAL_60_HZ,
290 cached_interval,
291 D3D12XBOX_FRAME_INTERVAL_FLAG_NONE
292 );
293 fb->screen->dev->ScheduleFrameEventX(
294 D3D12XBOX_FRAME_EVENT_ORIGIN,
295 0,
296 nullptr,
297 D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE
298 );
299
300 return &fb->base;
301 }
302