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 <dxgi1_4.h>
30 #include <directx/d3d12.h>
31 #include <wrl.h>
32 #include <dxguids/dxguids.h>
33
34 #include "util/u_memory.h"
35 #include "util/u_inlines.h"
36 #include "frontend/api.h"
37 #include "frontend/winsys_handle.h"
38
39 #include "stw_device.h"
40 #include "stw_pixelformat.h"
41 #include "stw_winsys.h"
42
43 #include "d3d12/d3d12_format.h"
44 #include "d3d12/d3d12_resource.h"
45 #include "d3d12/d3d12_screen.h"
46
47 using Microsoft::WRL::ComPtr;
48 constexpr uint32_t num_buffers = 2;
49
50 struct d3d12_wgl_framebuffer {
51 struct stw_winsys_framebuffer base;
52
53 struct d3d12_screen *screen;
54 enum pipe_format pformat;
55 HWND window;
56 ComPtr<IDXGISwapChain3> swapchain;
57 struct pipe_resource *buffers[num_buffers];
58 bool single_buffered;
59 struct pipe_resource *offscreen_buffer;
60 };
61
62 static struct d3d12_wgl_framebuffer *
d3d12_wgl_framebuffer(struct stw_winsys_framebuffer * fb)63 d3d12_wgl_framebuffer(struct stw_winsys_framebuffer *fb)
64 {
65 return (struct d3d12_wgl_framebuffer *)fb;
66 }
67
68 static void
d3d12_wgl_framebuffer_destroy(struct stw_winsys_framebuffer * fb,pipe_context * ctx)69 d3d12_wgl_framebuffer_destroy(struct stw_winsys_framebuffer *fb,
70 pipe_context *ctx)
71 {
72 struct d3d12_wgl_framebuffer *framebuffer = d3d12_wgl_framebuffer(fb);
73 struct pipe_fence_handle *fence = NULL;
74
75 if (ctx) {
76 /* Ensure all resources are flushed */
77 ctx->flush(ctx, &fence, PIPE_FLUSH_HINT_FINISH);
78 if (fence) {
79 ctx->screen->fence_finish(ctx->screen, ctx, fence, OS_TIMEOUT_INFINITE);
80 ctx->screen->fence_reference(ctx->screen, &fence, NULL);
81 }
82 }
83
84 for (int i = 0; i < num_buffers; ++i) {
85 if (framebuffer->buffers[i]) {
86 d3d12_resource_release(d3d12_resource(framebuffer->buffers[i]));
87 pipe_resource_reference(&framebuffer->buffers[i], NULL);
88 }
89 }
90
91 if (framebuffer->offscreen_buffer) {
92 pipe_resource_reference(&framebuffer->offscreen_buffer, NULL);
93 }
94
95 delete framebuffer;
96 }
97
98 static void
d3d12_wgl_framebuffer_resize(stw_winsys_framebuffer * fb,pipe_context * ctx,pipe_resource * templ)99 d3d12_wgl_framebuffer_resize(stw_winsys_framebuffer *fb,
100 pipe_context *ctx,
101 pipe_resource *templ)
102 {
103 struct d3d12_wgl_framebuffer *framebuffer = d3d12_wgl_framebuffer(fb);
104 struct d3d12_dxgi_screen *screen = d3d12_dxgi_screen(framebuffer->screen);
105
106 DXGI_SWAP_CHAIN_DESC1 desc = {};
107 desc.BufferCount = num_buffers;
108 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
109 desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
110 desc.Format = d3d12_get_format(templ->format);
111 desc.Width = templ->width0;
112 desc.Height = templ->height0;
113 desc.SampleDesc.Count = 1;
114 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
115
116 framebuffer->pformat = templ->format;
117
118 if (!framebuffer->swapchain) {
119 ComPtr<IDXGISwapChain1> swapchain1;
120 if (FAILED(screen->factory->CreateSwapChainForHwnd(
121 screen->base.cmdqueue,
122 framebuffer->window,
123 &desc,
124 nullptr,
125 nullptr,
126 &swapchain1))) {
127 debug_printf("D3D12: failed to create swapchain");
128 return;
129 }
130
131 swapchain1.As(&framebuffer->swapchain);
132
133 screen->factory->MakeWindowAssociation(framebuffer->window,
134 DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_PRINT_SCREEN);
135 }
136 else {
137 struct pipe_fence_handle *fence = NULL;
138
139 /* Ensure all resources are flushed */
140 ctx->flush(ctx, &fence, PIPE_FLUSH_HINT_FINISH);
141 if (fence) {
142 ctx->screen->fence_finish(ctx->screen, ctx, fence, OS_TIMEOUT_INFINITE);
143 ctx->screen->fence_reference(ctx->screen, &fence, NULL);
144 }
145
146 for (int i = 0; i < num_buffers; ++i) {
147 if (framebuffer->buffers[i]) {
148 d3d12_resource_release(d3d12_resource(framebuffer->buffers[i]));
149 pipe_resource_reference(&framebuffer->buffers[i], NULL);
150 }
151 }
152 if (FAILED(framebuffer->swapchain->ResizeBuffers(num_buffers, desc.Width, desc.Height, desc.Format, desc.Flags))) {
153 debug_printf("D3D12: failed to resize swapchain");
154 }
155 }
156
157 for (uint32_t i = 0; i < num_buffers; ++i) {
158 ID3D12Resource *res;
159 framebuffer->swapchain->GetBuffer(i, IID_PPV_ARGS(&res));
160 if (!res)
161 continue;
162
163 struct winsys_handle handle;
164 memset(&handle, 0, sizeof(handle));
165 handle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
166 handle.format = framebuffer->pformat;
167 handle.com_obj = res;
168
169 D3D12_RESOURCE_DESC res_desc = GetDesc(res);
170
171 struct pipe_resource templ;
172 memset(&templ, 0, sizeof(templ));
173 templ.target = PIPE_TEXTURE_2D;
174 templ.format = framebuffer->pformat;
175 templ.width0 = res_desc.Width;
176 templ.height0 = res_desc.Height;
177 templ.depth0 = 1;
178 templ.array_size = res_desc.DepthOrArraySize;
179 templ.nr_samples = res_desc.SampleDesc.Count;
180 templ.last_level = res_desc.MipLevels - 1;
181 templ.bind = PIPE_BIND_DISPLAY_TARGET | PIPE_BIND_RENDER_TARGET;
182 templ.usage = PIPE_USAGE_DEFAULT;
183 templ.flags = 0;
184
185 pipe_resource_reference(&framebuffer->buffers[i],
186 screen->base.base.resource_from_handle(&screen->base.base, &templ, &handle,
187 PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE));
188 }
189
190 if (framebuffer->single_buffered) {
191 if (framebuffer->offscreen_buffer) {
192 pipe_resource_reference(&framebuffer->offscreen_buffer, NULL);
193 }
194 struct pipe_resource local_templ = *templ;
195 local_templ.bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
196 framebuffer->offscreen_buffer = screen->base.base.resource_create(&screen->base.base, &local_templ);
197 }
198 }
199
200 static bool
d3d12_wgl_framebuffer_present(stw_winsys_framebuffer * fb,int interval)201 d3d12_wgl_framebuffer_present(stw_winsys_framebuffer *fb, int interval)
202 {
203 auto framebuffer = d3d12_wgl_framebuffer(fb);
204 if (!framebuffer->swapchain) {
205 debug_printf("D3D12: Cannot present; no swapchain");
206 return false;
207 }
208
209 if (interval < 1)
210 return S_OK == framebuffer->swapchain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
211 else
212 return S_OK == framebuffer->swapchain->Present(interval, 0);
213 }
214
215 static struct pipe_resource *
d3d12_wgl_framebuffer_get_resource(struct stw_winsys_framebuffer * pframebuffer,st_attachment_type statt)216 d3d12_wgl_framebuffer_get_resource(struct stw_winsys_framebuffer *pframebuffer,
217 st_attachment_type statt)
218 {
219 auto framebuffer = d3d12_wgl_framebuffer(pframebuffer);
220
221 if (!framebuffer->swapchain)
222 return nullptr;
223
224 if (framebuffer->single_buffered) {
225 assert(statt == ST_ATTACHMENT_FRONT_LEFT);
226 assert(framebuffer->offscreen_buffer);
227 pipe_reference(NULL, &framebuffer->offscreen_buffer->reference);
228 return framebuffer->offscreen_buffer;
229 }
230
231 UINT index = framebuffer->swapchain->GetCurrentBackBufferIndex();
232 if (statt == ST_ATTACHMENT_FRONT_LEFT)
233 index = !index;
234
235 assert(framebuffer->buffers[index]);
236 pipe_reference(NULL, &framebuffer->buffers[index]->reference);
237 return framebuffer->buffers[index];
238 }
239
240 static void
d3d12_wgl_framebuffer_flush_frontbuffer(struct stw_winsys_framebuffer * pframebuffer,struct pipe_context * pipe)241 d3d12_wgl_framebuffer_flush_frontbuffer(struct stw_winsys_framebuffer *pframebuffer,
242 struct pipe_context *pipe)
243 {
244 auto framebuffer = d3d12_wgl_framebuffer(pframebuffer);
245 struct pipe_blit_info blit;
246
247 memset(&blit, 0, sizeof(blit));
248 uint32_t index = framebuffer->swapchain->GetCurrentBackBufferIndex();
249 blit.dst.resource = framebuffer->buffers[index];
250 blit.dst.box.width = blit.dst.resource->width0;
251 blit.dst.box.height = blit.dst.resource->height0;
252 blit.dst.box.depth = 1;
253 blit.dst.format = blit.dst.resource->format;
254 blit.src.resource = framebuffer->offscreen_buffer;
255 blit.src.box.width = blit.src.resource->width0;
256 blit.src.box.height = blit.src.resource->height0;
257 blit.src.box.depth = 1;
258 blit.src.format = blit.src.resource->format;
259 blit.mask = PIPE_MASK_RGBA;
260 blit.filter = PIPE_TEX_FILTER_NEAREST;
261
262 pipe->blit(pipe, &blit);
263 pipe->flush_resource(pipe, blit.dst.resource);
264 pipe->flush(pipe, NULL, 0);
265 }
266
267 struct stw_winsys_framebuffer *
d3d12_wgl_create_framebuffer(struct pipe_screen * screen,HWND hWnd,int iPixelFormat)268 d3d12_wgl_create_framebuffer(struct pipe_screen *screen,
269 HWND hWnd,
270 int iPixelFormat)
271 {
272 const struct stw_pixelformat_info *pfi =
273 stw_pixelformat_get_info(iPixelFormat);
274 if ((pfi->pfd.dwFlags & PFD_SUPPORT_GDI))
275 return NULL;
276
277 if (pfi->stvis.color_format != PIPE_FORMAT_B8G8R8A8_UNORM &&
278 pfi->stvis.color_format != PIPE_FORMAT_R8G8B8A8_UNORM &&
279 pfi->stvis.color_format != PIPE_FORMAT_R10G10B10A2_UNORM &&
280 pfi->stvis.color_format != PIPE_FORMAT_R16G16B16A16_FLOAT)
281 return NULL;
282
283 struct d3d12_wgl_framebuffer *fb = CALLOC_STRUCT(d3d12_wgl_framebuffer);
284 if (!fb)
285 return NULL;
286
287 new (fb) struct d3d12_wgl_framebuffer();
288
289 fb->window = hWnd;
290 fb->screen = d3d12_screen(screen);
291 fb->single_buffered = (pfi->pfd.dwFlags & PFD_DOUBLEBUFFER) == 0;
292 fb->base.destroy = d3d12_wgl_framebuffer_destroy;
293 fb->base.resize = d3d12_wgl_framebuffer_resize;
294 fb->base.present = d3d12_wgl_framebuffer_present;
295 fb->base.get_resource = d3d12_wgl_framebuffer_get_resource;
296 fb->base.flush_frontbuffer = d3d12_wgl_framebuffer_flush_frontbuffer;
297
298 return &fb->base;
299 }
300