d3d11_shaders: handle shaders to render NV12 to NV12
[vlc.git] / modules / video_output / win32 / d3d11_quad.c
blob8c7be98104a966de597dda3592b2e2afa7ad4993
1 /*****************************************************************************
2 * d3d11_quad.c: Direct3D11 Qaud handling
3 *****************************************************************************
4 * Copyright (C) 2017-2018 VLC authors and VideoLAN
6 * Authors: Steve Lhomme <robux4@gmail.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 #include <assert.h>
28 #include <vlc_common.h>
30 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < _WIN32_WINNT_WIN7
31 # undef _WIN32_WINNT
32 # define _WIN32_WINNT _WIN32_WINNT_WIN7
33 #endif
35 #define COBJMACROS
36 #include <d3d11.h>
38 #include "d3d11_quad.h"
40 #define SPHERE_SLICES 128
41 #define nbLatBands SPHERE_SLICES
42 #define nbLonBands SPHERE_SLICES
44 void D3D11_RenderQuad(d3d11_device_t *d3d_dev, d3d_quad_t *quad,
45 ID3D11ShaderResourceView *resourceView[D3D11_MAX_SHADER_VIEW],
46 ID3D11RenderTargetView *d3drenderTargetView[D3D11_MAX_SHADER_VIEW])
48 UINT offset = 0;
50 /* Render the quad */
51 ID3D11DeviceContext_IASetPrimitiveTopology(d3d_dev->d3dcontext, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
53 /* vertex shader */
54 ID3D11DeviceContext_IASetInputLayout(d3d_dev->d3dcontext, quad->pVertexLayout);
55 ID3D11DeviceContext_IASetVertexBuffers(d3d_dev->d3dcontext, 0, 1, &quad->pVertexBuffer, &quad->vertexStride, &offset);
56 ID3D11DeviceContext_IASetIndexBuffer(d3d_dev->d3dcontext, quad->pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
57 if ( quad->pVertexShaderConstants )
58 ID3D11DeviceContext_VSSetConstantBuffers(d3d_dev->d3dcontext, 0, 1, &quad->pVertexShaderConstants);
60 ID3D11DeviceContext_VSSetShader(d3d_dev->d3dcontext, quad->d3dvertexShader, NULL, 0);
62 /* pixel shader */
63 ID3D11DeviceContext_PSSetConstantBuffers(d3d_dev->d3dcontext, 0, quad->PSConstantsCount, quad->pPixelShaderConstants);
64 assert(quad->resourceCount <= D3D11_MAX_SHADER_VIEW);
65 ID3D11DeviceContext_PSSetShaderResources(d3d_dev->d3dcontext, 0, quad->resourceCount, resourceView);
67 for (size_t i=0; i<D3D11_MAX_SHADER_VIEW; i++)
69 if (!d3drenderTargetView[i])
70 break;
72 ID3D11DeviceContext_PSSetShader(d3d_dev->d3dcontext, quad->d3dpixelShader[i], NULL, 0);
74 ID3D11DeviceContext_RSSetViewports(d3d_dev->d3dcontext, 1, &quad->cropViewport[i]);
76 ID3D11DeviceContext_OMSetRenderTargets(d3d_dev->d3dcontext, 1, &d3drenderTargetView[i], NULL);
78 ID3D11DeviceContext_DrawIndexed(d3d_dev->d3dcontext, quad->indexCount, 0, 0);
82 static bool AllocQuadVertices(vlc_object_t *o, d3d11_device_t *d3d_dev, d3d_quad_t *quad)
84 HRESULT hr;
86 switch (quad->projection)
88 case PROJECTION_MODE_RECTANGULAR:
89 quad->vertexCount = 4;
90 quad->indexCount = 2 * 3;
91 break;
92 case PROJECTION_MODE_EQUIRECTANGULAR:
93 quad->vertexCount = (SPHERE_SLICES + 1) * (SPHERE_SLICES + 1);
94 quad->indexCount = nbLatBands * nbLonBands * 2 * 3;
95 break;
96 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
97 quad->vertexCount = 4 * 6;
98 quad->indexCount = 6 * 2 * 3;
99 break;
100 default:
101 msg_Warn(o, "Projection mode %d not handled", quad->projection);
102 return false;
105 quad->vertexStride = sizeof(d3d_vertex_t);
107 D3D11_BUFFER_DESC bd;
108 memset(&bd, 0, sizeof(bd));
109 bd.Usage = D3D11_USAGE_DYNAMIC;
110 bd.ByteWidth = quad->vertexStride * quad->vertexCount;
111 bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
112 bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
114 hr = ID3D11Device_CreateBuffer(d3d_dev->d3ddevice, &bd, NULL, &quad->pVertexBuffer);
115 if(FAILED(hr)) {
116 msg_Err(o, "Failed to create vertex buffer. (hr=%lX)", hr);
117 return false;
120 /* create the index of the vertices */
121 D3D11_BUFFER_DESC quadDesc = {
122 .Usage = D3D11_USAGE_DYNAMIC,
123 .ByteWidth = sizeof(WORD) * quad->indexCount,
124 .BindFlags = D3D11_BIND_INDEX_BUFFER,
125 .CPUAccessFlags = D3D11_CPU_ACCESS_WRITE,
128 hr = ID3D11Device_CreateBuffer(d3d_dev->d3ddevice, &quadDesc, NULL, &quad->pIndexBuffer);
129 if(FAILED(hr)) {
130 msg_Err(o, "Could not create the quad indices. (hr=0x%lX)", hr);
131 ID3D11Buffer_Release(quad->pVertexBuffer);
132 quad->pVertexBuffer = NULL;
133 return false;
136 return true;
139 void D3D11_ReleaseQuad(d3d_quad_t *quad)
141 if (quad->pPixelShaderConstants[0])
143 ID3D11Buffer_Release(quad->pPixelShaderConstants[0]);
144 quad->pPixelShaderConstants[0] = NULL;
146 if (quad->pPixelShaderConstants[1])
148 ID3D11Buffer_Release(quad->pPixelShaderConstants[1]);
149 quad->pPixelShaderConstants[1] = NULL;
151 if (quad->pVertexBuffer)
153 ID3D11Buffer_Release(quad->pVertexBuffer);
154 quad->pVertexBuffer = NULL;
156 quad->d3dvertexShader = NULL;
157 if (quad->pIndexBuffer)
159 ID3D11Buffer_Release(quad->pIndexBuffer);
160 quad->pIndexBuffer = NULL;
162 if (quad->pVertexShaderConstants)
164 ID3D11Buffer_Release(quad->pVertexShaderConstants);
165 quad->pVertexShaderConstants = NULL;
167 for (size_t i=0; i<D3D11_MAX_SHADER_VIEW; i++)
169 if (quad->d3dpixelShader[i])
171 ID3D11PixelShader_Release(quad->d3dpixelShader[i]);
172 quad->d3dpixelShader[i] = NULL;
175 ReleasePictureSys(&quad->picSys);
179 * Compute the vertex ordering needed to rotate the video. Without
180 * rotation, the vertices of the rectangle are defined in a counterclockwise
181 * order. This function computes a remapping of the coordinates to
182 * implement the rotation, given fixed texture coordinates.
183 * The unrotated order is the following:
184 * 3--2
185 * | |
186 * 0--1
187 * For a 180 degrees rotation it should like this:
188 * 1--0
189 * | |
190 * 2--3
191 * Vertex 0 should be assigned coordinates at index 2 from the
192 * unrotated order and so on, thus yielding order: 2 3 0 1.
194 static void orientationVertexOrder(video_orientation_t orientation, int vertex_order[static 4])
196 switch (orientation) {
197 case ORIENT_ROTATED_90:
198 vertex_order[0] = 3;
199 vertex_order[1] = 0;
200 vertex_order[2] = 1;
201 vertex_order[3] = 2;
202 break;
203 case ORIENT_ROTATED_270:
204 vertex_order[0] = 1;
205 vertex_order[1] = 2;
206 vertex_order[2] = 3;
207 vertex_order[3] = 0;
208 break;
209 case ORIENT_ROTATED_180:
210 vertex_order[0] = 2;
211 vertex_order[1] = 3;
212 vertex_order[2] = 0;
213 vertex_order[3] = 1;
214 break;
215 case ORIENT_TRANSPOSED:
216 vertex_order[0] = 2;
217 vertex_order[1] = 1;
218 vertex_order[2] = 0;
219 vertex_order[3] = 3;
220 break;
221 case ORIENT_HFLIPPED:
222 vertex_order[0] = 1;
223 vertex_order[1] = 0;
224 vertex_order[2] = 3;
225 vertex_order[3] = 2;
226 break;
227 case ORIENT_VFLIPPED:
228 vertex_order[0] = 3;
229 vertex_order[1] = 2;
230 vertex_order[2] = 1;
231 vertex_order[3] = 0;
232 break;
233 case ORIENT_ANTI_TRANSPOSED: /* transpose + vflip */
234 vertex_order[0] = 0;
235 vertex_order[1] = 3;
236 vertex_order[2] = 2;
237 vertex_order[3] = 1;
238 break;
239 default:
240 vertex_order[0] = 0;
241 vertex_order[1] = 1;
242 vertex_order[2] = 2;
243 vertex_order[3] = 3;
244 break;
248 static void SetupQuadFlat(d3d_vertex_t *dst_data, const RECT *output,
249 const d3d_quad_t *quad,
250 WORD *triangle_pos, video_orientation_t orientation)
252 unsigned int src_width = quad->i_width;
253 unsigned int src_height = quad->i_height;
254 float MidX,MidY;
256 float top, bottom, left, right;
257 /* find the middle of the visible part of the texture, it will be a 0,0
258 * the rest of the visible area must correspond to -1,1 */
259 switch (orientation)
261 case ORIENT_ROTATED_90: /* 90° anti clockwise */
262 /* right/top aligned */
263 MidY = (output->left + output->right) / 2.f;
264 MidX = (output->top + output->bottom) / 2.f;
265 top = MidY / (MidY - output->top);
266 bottom = -(src_height - MidX) / (MidX - output->top);
267 left = (MidX - src_height) / (MidX - output->left);
268 right = MidX / (MidX - (src_width - output->right));
269 break;
270 case ORIENT_ROTATED_180: /* 180° */
271 /* right/top aligned */
272 MidY = (output->top + output->bottom) / 2.f;
273 MidX = (output->left + output->right) / 2.f;
274 top = (src_height - MidY) / (output->bottom - MidY);
275 bottom = -MidY / (MidY - output->top);
276 left = -MidX / (MidX - output->left);
277 right = (src_width - MidX) / (output->right - MidX);
278 break;
279 case ORIENT_ROTATED_270: /* 90° clockwise */
280 /* right/top aligned */
281 MidY = (output->left + output->right) / 2.f;
282 MidX = (output->top + output->bottom) / 2.f;
283 top = (src_width - MidX) / (output->right - MidX);
284 bottom = -MidY / (MidY - output->top);
285 left = -MidX / (MidX - output->left);
286 right = (src_height - MidY) / (output->bottom - MidY);
287 break;
288 case ORIENT_ANTI_TRANSPOSED:
289 MidY = (output->left + output->right) / 2.f;
290 MidX = (output->top + output->bottom) / 2.f;
291 top = (src_width - MidX) / (output->right - MidX);
292 bottom = -MidY / (MidY - output->top);
293 left = -(src_height - MidY) / (output->bottom - MidY);
294 right = MidX / (MidX - output->left);
295 break;
296 case ORIENT_TRANSPOSED:
297 MidY = (output->left + output->right) / 2.f;
298 MidX = (output->top + output->bottom) / 2.f;
299 top = (src_width - MidX) / (output->right - MidX);
300 bottom = -MidY / (MidY - output->top);
301 left = -MidX / (MidX - output->left);
302 right = (src_height - MidY) / (output->bottom - MidY);
303 break;
304 case ORIENT_VFLIPPED:
305 MidY = (output->top + output->bottom) / 2.f;
306 MidX = (output->left + output->right) / 2.f;
307 top = (src_height - MidY) / (output->bottom - MidY);
308 bottom = -MidY / (MidY - output->top);
309 left = -MidX / (MidX - output->left);
310 right = (src_width - MidX) / (output->right - MidX);
311 break;
312 case ORIENT_HFLIPPED:
313 MidY = (output->top + output->bottom) / 2.f;
314 MidX = (output->left + output->right) / 2.f;
315 top = MidY / (MidY - output->top);
316 bottom = -(src_height - MidY) / (output->bottom - MidY);
317 left = -(src_width - MidX) / (output->right - MidX);
318 right = MidX / (MidX - output->left);
319 break;
320 case ORIENT_NORMAL:
321 default:
322 /* left/top aligned */
323 MidY = (output->top + output->bottom) / 2.f;
324 MidX = (output->left + output->right) / 2.f;
325 top = MidY / (MidY - output->top);
326 bottom = -(src_height - MidY) / (output->bottom - MidY);
327 left = -MidX / (MidX - output->left);
328 right = (src_width - MidX) / (output->right - MidX);
329 break;
332 const float vertices_coords[4][2] = {
333 { left, bottom },
334 { right, bottom },
335 { right, top },
336 { left, top },
339 /* Compute index remapping necessary to implement the rotation. */
340 int vertex_order[4];
341 orientationVertexOrder(orientation, vertex_order);
343 for (int i = 0; i < 4; ++i) {
344 dst_data[i].position.x = vertices_coords[vertex_order[i]][0];
345 dst_data[i].position.y = vertices_coords[vertex_order[i]][1];
348 // bottom left
349 dst_data[0].position.z = 0.0f;
350 dst_data[0].texture.x = 0.0f;
351 dst_data[0].texture.y = 1.0f;
353 // bottom right
354 dst_data[1].position.z = 0.0f;
355 dst_data[1].texture.x = 1.0f;
356 dst_data[1].texture.y = 1.0f;
358 // top right
359 dst_data[2].position.z = 0.0f;
360 dst_data[2].texture.x = 1.0f;
361 dst_data[2].texture.y = 0.0f;
363 // top left
364 dst_data[3].position.z = 0.0f;
365 dst_data[3].texture.x = 0.0f;
366 dst_data[3].texture.y = 0.0f;
368 /* Make sure surfaces are facing the right way */
369 if( orientation == ORIENT_TOP_RIGHT || orientation == ORIENT_BOTTOM_LEFT
370 || orientation == ORIENT_LEFT_TOP || orientation == ORIENT_RIGHT_BOTTOM )
372 triangle_pos[0] = 0;
373 triangle_pos[1] = 1;
374 triangle_pos[2] = 3;
376 triangle_pos[3] = 3;
377 triangle_pos[4] = 1;
378 triangle_pos[5] = 2;
380 else
382 triangle_pos[0] = 3;
383 triangle_pos[1] = 1;
384 triangle_pos[2] = 0;
386 triangle_pos[3] = 2;
387 triangle_pos[4] = 1;
388 triangle_pos[5] = 3;
392 static void SetupQuadSphere(d3d_vertex_t *dst_data, const RECT *output,
393 const d3d_quad_t *quad, WORD *triangle_pos)
395 const float scaleX = (float)(output->right - output->left) / quad->i_width;
396 const float scaleY = (float)(output->bottom - output->top) / quad->i_height;
397 for (unsigned lat = 0; lat <= nbLatBands; lat++) {
398 float theta = lat * (float) M_PI / nbLatBands;
399 float sinTheta, cosTheta;
401 sincosf(theta, &sinTheta, &cosTheta);
403 for (unsigned lon = 0; lon <= nbLonBands; lon++) {
404 float phi = lon * 2 * (float) M_PI / nbLonBands;
405 float sinPhi, cosPhi;
407 sincosf(phi, &sinPhi, &cosPhi);
409 float x = cosPhi * sinTheta;
410 float y = cosTheta;
411 float z = sinPhi * sinTheta;
413 unsigned off1 = lat * (nbLonBands + 1) + lon;
414 dst_data[off1].position.x = SPHERE_RADIUS * x;
415 dst_data[off1].position.y = SPHERE_RADIUS * y;
416 dst_data[off1].position.z = SPHERE_RADIUS * z;
418 dst_data[off1].texture.x = scaleX * lon / (float) nbLonBands; // 0(left) to 1(right)
419 dst_data[off1].texture.y = scaleY * lat / (float) nbLatBands; // 0(top) to 1 (bottom)
423 for (unsigned lat = 0; lat < nbLatBands; lat++) {
424 for (unsigned lon = 0; lon < nbLonBands; lon++) {
425 unsigned first = (lat * (nbLonBands + 1)) + lon;
426 unsigned second = first + nbLonBands + 1;
428 unsigned off = (lat * nbLatBands + lon) * 3 * 2;
430 triangle_pos[off] = first;
431 triangle_pos[off + 1] = first + 1;
432 triangle_pos[off + 2] = second;
434 triangle_pos[off + 3] = second;
435 triangle_pos[off + 4] = first + 1;
436 triangle_pos[off + 5] = second + 1;
442 static void SetupQuadCube(d3d_vertex_t *dst_data, const RECT *output,
443 const d3d_quad_t *quad, WORD *triangle_pos)
445 static const float coord[] = {
446 -1.0, 1.0, -1.0f, // front
447 -1.0, -1.0, -1.0f,
448 1.0, 1.0, -1.0f,
449 1.0, -1.0, -1.0f,
451 -1.0, 1.0, 1.0f, // back
452 -1.0, -1.0, 1.0f,
453 1.0, 1.0, 1.0f,
454 1.0, -1.0, 1.0f,
456 -1.0, 1.0, -1.0f, // left
457 -1.0, -1.0, -1.0f,
458 -1.0, 1.0, 1.0f,
459 -1.0, -1.0, 1.0f,
461 1.0f, 1.0, -1.0f, // right
462 1.0f, -1.0, -1.0f,
463 1.0f, 1.0, 1.0f,
464 1.0f, -1.0, 1.0f,
466 -1.0, -1.0, 1.0f, // bottom
467 -1.0, -1.0, -1.0f,
468 1.0, -1.0, 1.0f,
469 1.0, -1.0, -1.0f,
471 -1.0, 1.0, 1.0f, // top
472 -1.0, 1.0, -1.0f,
473 1.0, 1.0, 1.0f,
474 1.0, 1.0, -1.0f,
477 const float scaleX = (float)(output->right - output->left) / quad->i_width;
478 const float scaleY = (float)(output->bottom - output->top) / quad->i_height;
480 const float col[] = {0.f, scaleX / 3, scaleX * 2 / 3, scaleX};
481 const float row[] = {0.f, scaleY / 2, scaleY};
483 const float tex[] = {
484 col[1], row[1], // front
485 col[1], row[2],
486 col[2], row[1],
487 col[2], row[2],
489 col[3], row[1], // back
490 col[3], row[2],
491 col[2], row[1],
492 col[2], row[2],
494 col[2], row[0], // left
495 col[2], row[1],
496 col[1], row[0],
497 col[1], row[1],
499 col[0], row[0], // right
500 col[0], row[1],
501 col[1], row[0],
502 col[1], row[1],
504 col[0], row[2], // bottom
505 col[0], row[1],
506 col[1], row[2],
507 col[1], row[1],
509 col[2], row[0], // top
510 col[2], row[1],
511 col[3], row[0],
512 col[3], row[1],
515 const unsigned i_nbVertices = ARRAY_SIZE(coord) / 3;
517 for (unsigned v = 0; v < i_nbVertices; ++v)
519 dst_data[v].position.x = coord[3 * v];
520 dst_data[v].position.y = coord[3 * v + 1];
521 dst_data[v].position.z = coord[3 * v + 2];
523 dst_data[v].texture.x = tex[2 * v];
524 dst_data[v].texture.y = tex[2 * v + 1];
527 const WORD ind[] = {
528 2, 1, 0, 3, 1, 2, // front
529 4, 7, 6, 5, 7, 4, // back
530 8, 11, 10, 9, 11, 8, // left
531 14, 13, 12, 15, 13, 14, // right
532 16, 19, 18, 17, 19, 16, // bottom
533 22, 21, 20, 23, 21, 22, // top
536 memcpy(triangle_pos, ind, sizeof(ind));
539 #undef D3D11_UpdateQuadPosition
540 bool D3D11_UpdateQuadPosition( vlc_object_t *o, d3d11_device_t *d3d_dev, d3d_quad_t *quad,
541 const RECT *output, video_orientation_t orientation )
543 HRESULT hr;
544 D3D11_MAPPED_SUBRESOURCE mappedResource;
546 if (unlikely(quad->pVertexBuffer == NULL))
547 return false;
549 /* create the vertices */
550 hr = ID3D11DeviceContext_Map(d3d_dev->d3dcontext, (ID3D11Resource *)quad->pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
551 if (FAILED(hr)) {
552 msg_Err(o, "Failed to lock the vertex buffer (hr=0x%lX)", hr);
553 return false;
555 d3d_vertex_t *dst_data = mappedResource.pData;
557 /* create the vertex indices */
558 hr = ID3D11DeviceContext_Map(d3d_dev->d3dcontext, (ID3D11Resource *)quad->pIndexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
559 if (FAILED(hr)) {
560 msg_Err(o, "Failed to lock the index buffer (hr=0x%lX)", hr);
561 ID3D11DeviceContext_Unmap(d3d_dev->d3dcontext, (ID3D11Resource *)quad->pVertexBuffer, 0);
562 return false;
564 WORD *triangle_pos = mappedResource.pData;
566 switch (quad->projection)
568 case PROJECTION_MODE_RECTANGULAR:
569 SetupQuadFlat(dst_data, output, quad, triangle_pos, orientation);
570 break;
571 case PROJECTION_MODE_EQUIRECTANGULAR:
572 SetupQuadSphere(dst_data, output, quad, triangle_pos);
573 break;
574 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
575 SetupQuadCube(dst_data, output, quad, triangle_pos);
576 break;
577 default:
578 msg_Warn(o, "Projection mode %d not handled", quad->projection);
579 return false;
582 ID3D11DeviceContext_Unmap(d3d_dev->d3dcontext, (ID3D11Resource *)quad->pIndexBuffer, 0);
583 ID3D11DeviceContext_Unmap(d3d_dev->d3dcontext, (ID3D11Resource *)quad->pVertexBuffer, 0);
585 return true;
588 static bool ShaderUpdateConstants(vlc_object_t *o, d3d11_device_t *d3d_dev, d3d_quad_t *quad)
590 D3D11_MAPPED_SUBRESOURCE mappedResource;
591 HRESULT hr = ID3D11DeviceContext_Map(d3d_dev->d3dcontext, (ID3D11Resource *)quad->pPixelShaderConstants[0], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
592 if (FAILED(hr))
594 msg_Err(o, "Failed to lock the picture shader constants (hr=0x%lX)", hr);
595 return false;
598 PS_CONSTANT_BUFFER *dst_data = mappedResource.pData;
599 *dst_data = quad->shaderConstants;
600 ID3D11DeviceContext_Unmap(d3d_dev->d3dcontext, (ID3D11Resource *)quad->pPixelShaderConstants[0], 0);
601 return true;
604 #undef D3D11_UpdateQuadOpacity
605 void D3D11_UpdateQuadOpacity(vlc_object_t *o, d3d11_device_t *d3d_dev, d3d_quad_t *quad, float opacity)
607 if (quad->shaderConstants.Opacity == opacity)
608 return;
610 float old = quad->shaderConstants.Opacity;
611 quad->shaderConstants.Opacity = opacity;
612 if (!ShaderUpdateConstants(o, d3d_dev, quad))
613 quad->shaderConstants.Opacity = old;
616 #undef D3D11_UpdateQuadLuminanceScale
617 void D3D11_UpdateQuadLuminanceScale(vlc_object_t *o, d3d11_device_t *d3d_dev, d3d_quad_t *quad, float luminanceScale)
619 if (quad->shaderConstants.LuminanceScale == luminanceScale)
620 return;
622 float old = quad->shaderConstants.LuminanceScale;
623 quad->shaderConstants.LuminanceScale = luminanceScale;
624 if (!ShaderUpdateConstants(o, d3d_dev, quad))
625 quad->shaderConstants.LuminanceScale = old;
628 #undef D3D11_SetupQuad
629 int D3D11_SetupQuad(vlc_object_t *o, d3d11_device_t *d3d_dev, const video_format_t *fmt, d3d_quad_t *quad,
630 const display_info_t *displayFormat, const RECT *output,
631 ID3D11VertexShader *d3dvertexShader, ID3D11InputLayout *pVertexLayout,
632 video_projection_mode_t projection, video_orientation_t orientation)
634 HRESULT hr;
635 const bool RGB_shader = IsRGBShader(quad->formatInfo);
637 quad->shaderConstants.LuminanceScale = GetFormatLuminance(o, fmt) / (float)displayFormat->luminance_peak;
639 /* pixel shader constant buffer */
640 quad->shaderConstants.Opacity = 1.0;
641 if (fmt->i_visible_width == fmt->i_width)
642 quad->shaderConstants.BoundaryX = 1.0; /* let texture clamping happen */
643 else
644 quad->shaderConstants.BoundaryX = (FLOAT) (fmt->i_visible_width - 1) / fmt->i_width;
645 if (fmt->i_visible_height == fmt->i_height)
646 quad->shaderConstants.BoundaryY = 1.0; /* let texture clamping happen */
647 else
648 quad->shaderConstants.BoundaryY = (FLOAT) (fmt->i_visible_height - 1) / fmt->i_height;
650 static_assert((sizeof(PS_CONSTANT_BUFFER)%16)==0,"Constant buffers require 16-byte alignment");
651 D3D11_BUFFER_DESC constantDesc = {
652 .Usage = D3D11_USAGE_DYNAMIC,
653 .ByteWidth = sizeof(PS_CONSTANT_BUFFER),
654 .BindFlags = D3D11_BIND_CONSTANT_BUFFER,
655 .CPUAccessFlags = D3D11_CPU_ACCESS_WRITE,
657 D3D11_SUBRESOURCE_DATA constantInit = { .pSysMem = &quad->shaderConstants };
658 hr = ID3D11Device_CreateBuffer(d3d_dev->d3ddevice, &constantDesc, &constantInit, &quad->pPixelShaderConstants[0]);
659 if(FAILED(hr)) {
660 msg_Err(o, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr);
661 goto error;
664 FLOAT itu_black_level = 0.f;
665 FLOAT itu_achromacy = 0.f;
666 if (!RGB_shader)
668 switch (quad->formatInfo->bitsPerChannel)
670 case 8:
671 /* Rec. ITU-R BT.709-6 §4.6 */
672 itu_black_level = 16.f / 255.f;
673 itu_achromacy = 128.f / 255.f;
674 break;
675 case 10:
676 /* Rec. ITU-R BT.709-6 §4.6 */
677 itu_black_level = 64.f / 1023.f;
678 itu_achromacy = 512.f / 1023.f;
679 break;
680 case 12:
681 /* Rec. ITU-R BT.2020-2 Table 5 */
682 itu_black_level = 256.f / 4095.f;
683 itu_achromacy = 2048.f / 4095.f;
684 break;
685 default:
686 /* unknown bitdepth, use approximation for infinite bit depth */
687 itu_black_level = 16.f / 256.f;
688 itu_achromacy = 128.f / 256.f;
689 break;
693 static const FLOAT IDENTITY_4X4[4 * 4] = {
694 1.f, 0.f, 0.f, 0.f,
695 0.f, 1.f, 0.f, 0.f,
696 0.f, 0.f, 1.f, 0.f,
697 0.f, 0.f, 0.f, 1.f,
700 /* matrices for studio range */
701 /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion, in studio range */
702 static const FLOAT COLORSPACE_BT601_YUV_TO_FULL_RGBA[4*4] = {
703 1.164383561643836f, 0.f, 1.596026785714286f, 0.f,
704 1.164383561643836f, -0.391762290094914f, -0.812967647237771f, 0.f,
705 1.164383561643836f, 2.017232142857142f, 0.f, 0.f,
706 0.f, 0.f, 0.f, 1.f,
708 /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.709_conversion, in studio range */
709 static const FLOAT COLORSPACE_BT709_YUV_TO_FULL_RGBA[4*4] = {
710 1.164383561643836f, 0.f, 1.792741071428571f, 0.f,
711 1.164383561643836f, -0.213248614273730f, -0.532909328559444f, 0.f,
712 1.164383561643836f, 2.112401785714286f, 0.f, 0.f,
713 0.f, 0.f, 0.f, 1.f,
715 /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion, in studio range */
716 static const FLOAT COLORSPACE_BT2020_YUV_TO_FULL_RGBA[4*4] = {
717 1.164383561643836f, 0.000000000000f, 1.678674107143f, 0.f,
718 1.164383561643836f, -0.127007098661f, -0.440987687946f, 0.f,
719 1.164383561643836f, 2.141772321429f, 0.000000000000f, 0.f,
720 0.f, 0.f, 0.f, 1.f,
723 PS_COLOR_TRANSFORM colorspace;
725 memcpy(colorspace.WhitePoint, IDENTITY_4X4, sizeof(colorspace.WhitePoint));
727 const FLOAT *ppColorspace;
728 if (RGB_shader)
729 ppColorspace = IDENTITY_4X4;
730 else {
731 switch (fmt->space){
732 case COLOR_SPACE_BT709:
733 ppColorspace = COLORSPACE_BT709_YUV_TO_FULL_RGBA;
734 break;
735 case COLOR_SPACE_BT2020:
736 ppColorspace = COLORSPACE_BT2020_YUV_TO_FULL_RGBA;
737 break;
738 case COLOR_SPACE_BT601:
739 ppColorspace = COLORSPACE_BT601_YUV_TO_FULL_RGBA;
740 break;
741 default:
742 case COLOR_SPACE_UNDEF:
743 if( fmt->i_height > 576 )
744 ppColorspace = COLORSPACE_BT709_YUV_TO_FULL_RGBA;
745 else
746 ppColorspace = COLORSPACE_BT601_YUV_TO_FULL_RGBA;
747 break;
749 /* all matrices work in studio range and output in full range */
750 colorspace.WhitePoint[0*4 + 3] = -itu_black_level;
751 colorspace.WhitePoint[1*4 + 3] = -itu_achromacy;
752 colorspace.WhitePoint[2*4 + 3] = -itu_achromacy;
755 memcpy(colorspace.Colorspace, ppColorspace, sizeof(colorspace.Colorspace));
757 constantInit.pSysMem = &colorspace;
759 static_assert((sizeof(PS_COLOR_TRANSFORM)%16)==0,"Constant buffers require 16-byte alignment");
760 constantDesc.ByteWidth = sizeof(PS_COLOR_TRANSFORM);
761 hr = ID3D11Device_CreateBuffer(d3d_dev->d3ddevice, &constantDesc, &constantInit, &quad->pPixelShaderConstants[1]);
762 if(FAILED(hr)) {
763 msg_Err(o, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr);
764 goto error;
766 quad->PSConstantsCount = 2;
767 quad->projection = projection;
769 /* vertex shader constant buffer */
770 if (projection == PROJECTION_MODE_EQUIRECTANGULAR
771 || projection == PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD)
773 constantDesc.ByteWidth = sizeof(VS_PROJECTION_CONST);
774 static_assert((sizeof(VS_PROJECTION_CONST)%16)==0,"Constant buffers require 16-byte alignment");
775 hr = ID3D11Device_CreateBuffer(d3d_dev->d3ddevice, &constantDesc, NULL, &quad->pVertexShaderConstants);
776 if(FAILED(hr)) {
777 msg_Err(o, "Could not create the vertex shader constant buffer. (hr=0x%lX)", hr);
778 goto error;
782 quad->picSys.formatTexture = quad->formatInfo->formatTexture;
783 quad->picSys.context = d3d_dev->d3dcontext;
784 ID3D11DeviceContext_AddRef(quad->picSys.context);
786 if (!AllocQuadVertices(o, d3d_dev, quad))
787 goto error;
788 if (!D3D11_UpdateQuadPosition(o, d3d_dev, quad, output, orientation))
789 goto error;
791 for (size_t i=0; i<D3D11_MAX_SHADER_VIEW; i++)
793 quad->cropViewport[i].MinDepth = 0.0f;
794 quad->cropViewport[i].MaxDepth = 1.0f;
796 quad->d3dvertexShader = d3dvertexShader;
797 quad->pVertexLayout = pVertexLayout;
798 quad->resourceCount = DxgiResourceCount(quad->formatInfo);
800 return VLC_SUCCESS;
802 error:
803 D3D11_ReleaseQuad(quad);
804 return VLC_EGENERIC;
807 void D3D11_UpdateViewport(d3d_quad_t *quad, const RECT *rect)
809 quad->cropViewport[0].TopLeftX = rect->left;
810 quad->cropViewport[0].TopLeftY = rect->top;
811 quad->cropViewport[0].Width = rect->right - rect->left;
812 quad->cropViewport[0].Height = rect->bottom - rect->top;
814 if ( quad->formatInfo->formatTexture == DXGI_FORMAT_NV12 ||
815 quad->formatInfo->formatTexture == DXGI_FORMAT_P010 )
817 quad->cropViewport[1].TopLeftX = rect->left / 2;
818 quad->cropViewport[1].TopLeftY = rect->top / 2;
819 quad->cropViewport[1].Width = (rect->right - rect->left) / 2;
820 quad->cropViewport[1].Height = (rect->bottom - rect->top) / 2;