mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
D3D9: views
The HLSL and Cg drivers draw a core's views as gl1 does, through code they share in gfx/common/d3d9_views.h: each view is drawn straight from the frame's texture by a shader model 2 pixel shader that stops filtering at its edge, anaglyph and interlaced blend a canvas of both eyes, and the UI is drawn once into a layer placed in each eye. A preset draws the frame whole. A frame past the size the core declared grows the frame's textures instead of running past them. The views e2e cases run on D3D9 HLSL under Wine.
This commit is contained in:
1 parent
be85ba3e7a
commit
92d69e6025
7 files changed
+1060
-52
No files matched your search
@@ -1,24 +1,26 @@
|
||||
name: CI Windows views under Wine
|
||||
|
||||
# Views on the D3D10, D3D11 and D3D12 drivers, end to end: retroarch.exe
|
||||
# cross-built with mingw-w64 runs the video_views test core under Wine,
|
||||
# on a virtual display through Mesa's software rasterisers (D3D12 on
|
||||
# Wine's vkd3d over lavapipe), and samples/cores/video_views/e2e/run.py
|
||||
# checks the screen in each stereo mode, from the core's software
|
||||
# frames and its D3D11 and D3D12 textures, with the menu laid out per
|
||||
# eye and the pointer over it. Wine's own shader compiler can't take the
|
||||
# drivers' HLSL, so vkd3d's, from its release, is put in as
|
||||
# D3DCompiler_47.dll.
|
||||
# Views on the D3D9 HLSL, D3D10, D3D11 and D3D12 drivers, end to end:
|
||||
# retroarch.exe cross-built with mingw-w64 runs the video_views test core
|
||||
# under Wine, on a virtual display through Mesa's software rasterisers
|
||||
# (D3D12 on Wine's vkd3d over lavapipe), and
|
||||
# samples/cores/video_views/e2e/run.py checks the screen in each stereo
|
||||
# mode, from the core's software frames and its D3D11 and D3D12
|
||||
# textures, with the menu laid out per eye and the pointer over it.
|
||||
# Wine's own shader compiler can't take the drivers' HLSL, so vkd3d's,
|
||||
# from its release, is put in as D3DCompiler_47.dll.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'gfx/common/d3d9_*'
|
||||
- 'gfx/drivers/d3d9hlsl.c'
|
||||
- 'gfx/drivers/d3d10.c'
|
||||
- 'gfx/drivers/d3d11.c'
|
||||
- 'gfx/drivers/d3d12.c'
|
||||
- 'gfx/drivers/d3d_shaders/views_sm4.hlsl.h'
|
||||
- 'gfx/drivers/d3d_shaders/views_sm*.hlsl.h'
|
||||
- 'gfx/video_views.*'
|
||||
- 'gfx/video_views_draw.h'
|
||||
- 'gfx/video_driver.c'
|
||||
@@ -27,10 +29,12 @@ on:
|
||||
- '.github/workflows/Windows-wine-views.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'gfx/common/d3d9_*'
|
||||
- 'gfx/drivers/d3d9hlsl.c'
|
||||
- 'gfx/drivers/d3d10.c'
|
||||
- 'gfx/drivers/d3d11.c'
|
||||
- 'gfx/drivers/d3d12.c'
|
||||
- 'gfx/drivers/d3d_shaders/views_sm4.hlsl.h'
|
||||
- 'gfx/drivers/d3d_shaders/views_sm*.hlsl.h'
|
||||
- 'gfx/video_views.*'
|
||||
- 'gfx/video_views_draw.h'
|
||||
- 'gfx/video_driver.c'
|
||||
@@ -48,7 +52,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
windows-wine-views:
|
||||
name: Views on D3D10, D3D11 and D3D12 under Wine
|
||||
name: Views on D3D9, D3D10, D3D11 and D3D12 under Wine
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 150
|
||||
|
||||
@@ -117,7 +121,7 @@ jobs:
|
||||
cp .libs/libvkd3d-shader-1.dll .libs/libvkd3d-1.dll \
|
||||
"$GITHUB_WORKSPACE/"
|
||||
|
||||
- name: Build retroarch.exe with mingw-w64 (D3D10, D3D11, D3D12, slang)
|
||||
- name: Build retroarch.exe with mingw-w64 (D3D9, D3D10, D3D11, D3D12, slang)
|
||||
shell: bash
|
||||
run: |
|
||||
set -eu
|
||||
@@ -126,7 +130,6 @@ jobs:
|
||||
./configure --host=x86_64-w64-mingw32 \
|
||||
--disable-qt --disable-vulkan --disable-ffmpeg \
|
||||
--disable-cheevos --disable-discord \
|
||||
--disable-d3d9 \
|
||||
--disable-sdl --disable-sdl2
|
||||
make -j$(nproc) CC="ccache x86_64-w64-mingw32-gcc" \
|
||||
CXX="ccache x86_64-w64-mingw32-g++"
|
||||
@@ -137,7 +140,7 @@ jobs:
|
||||
done
|
||||
make -C samples/cores/video_views DLL=1 CC=x86_64-w64-mingw32-gcc
|
||||
|
||||
- name: Views on D3D10, D3D11 and D3D12, end to end (Wine, Xvfb, llvmpipe, lavapipe)
|
||||
- name: Views on D3D9, D3D10, D3D11 and D3D12, end to end (Wine, Xvfb, llvmpipe, lavapipe)
|
||||
shell: bash
|
||||
run: |
|
||||
set -eu
|
||||
@@ -147,7 +150,7 @@ jobs:
|
||||
# Initialize the prefix with a display and wait for setup to finish.
|
||||
WINEDEBUG=-all timeout 120 xvfb-run -a sh -c 'wineboot -i && wineserver -w'
|
||||
RA_E2E_WINE=1 python3 samples/cores/video_views/e2e/run.py \
|
||||
./retroarch.exe "$RUNNER_TEMP/views-e2e" d3d10 d3d11 d3d12
|
||||
./retroarch.exe "$RUNNER_TEMP/views-e2e" d3d9_hlsl d3d10 d3d11 d3d12
|
||||
|
||||
- name: Preserve views logs on failure
|
||||
if: failure()
|
||||
|
||||
@@ -31,6 +31,13 @@
|
||||
#include "d3d_common.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
/* Views on the PC: Xbox 360 cores have none */
|
||||
#ifndef _XBOX
|
||||
#define D3D9_VIEWS
|
||||
#include <retro_atomic.h>
|
||||
#include "../video_views.h"
|
||||
#endif
|
||||
|
||||
#ifdef _XBOX
|
||||
#define D3D9_RGB565_FORMAT D3DFMT_LIN_R5G6B5
|
||||
#define D3D9_ARGB8888_FORMAT D3DFMT_LIN_A8R8G8B8
|
||||
@@ -45,6 +52,51 @@
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#ifdef D3D9_VIEWS
|
||||
/* A render target views draw into: a texture and its surface */
|
||||
typedef struct d3d9_views_target
|
||||
{
|
||||
LPDIRECT3DTEXTURE9 tex;
|
||||
LPDIRECT3DSURFACE9 surf;
|
||||
} d3d9_views_target_t;
|
||||
|
||||
enum d3d9_views_ps
|
||||
{
|
||||
D3D9_VIEWS_PS_RECT = 0,
|
||||
D3D9_VIEWS_PS_BLEND,
|
||||
D3D9_VIEWS_PS_LAYER,
|
||||
D3D9_VIEWS_PS_COUNT
|
||||
};
|
||||
|
||||
typedef struct d3d9_views
|
||||
{
|
||||
video_views_chains_t chains;
|
||||
video_views_target_t canvas_target;
|
||||
video_views_target_t ui_target;
|
||||
video_views_ui_dims_t saved;
|
||||
d3d9_views_target_t canvas;
|
||||
d3d9_views_target_t ui;
|
||||
/* The render target the UI layer was entered from */
|
||||
LPDIRECT3DSURFACE9 back;
|
||||
/* The pixel shaders, the driver's handles, and each one's constant
|
||||
* register */
|
||||
void *ps[D3D9_VIEWS_PS_COUNT];
|
||||
int ps_reg[D3D9_VIEWS_PS_COUNT];
|
||||
unsigned frame_dims;
|
||||
/* For get_flags on the main thread: whether views may be drawn, and
|
||||
* whether the last frame with a map drew it whole */
|
||||
retro_atomic_int_t allowed;
|
||||
retro_atomic_int_t fallback;
|
||||
bool tried;
|
||||
bool failed;
|
||||
/* The canvas, and the UI layer with its alpha kept apart */
|
||||
bool blend;
|
||||
bool layer;
|
||||
bool active;
|
||||
bool direct_seen;
|
||||
} d3d9_views_t;
|
||||
#endif
|
||||
|
||||
typedef struct d3d9_video
|
||||
{
|
||||
overlay_t *menu;
|
||||
@@ -101,6 +153,9 @@ typedef struct d3d9_video
|
||||
* with a different `rgb32` value. Defaults to false; the first
|
||||
* call will see a NULL tex and create one regardless. */
|
||||
bool menu_tex_rgb32;
|
||||
#ifdef D3D9_VIEWS
|
||||
d3d9_views_t views;
|
||||
#endif
|
||||
} d3d9_video_t;
|
||||
|
||||
bool d3d9_create_device(void *dev,
|
||||
|
||||
@@ -0,0 +1,514 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2026 - The RetroArch team
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* D3D9's views, shared by its HLSL and Cg drivers, which differ only in
|
||||
* how a pixel shader is made and bound. Each view is drawn straight from
|
||||
* the frame's texture; anaglyph and interlaced blend a canvas of both
|
||||
* eyes; the UI is drawn once into a layer placed in each eye. Quads are
|
||||
* pretransformed, so no vertex shader is bound.
|
||||
*
|
||||
* Define before including, then include once per driver:
|
||||
* D3D9V_FN(name) the driver's prefix for name
|
||||
* D3D9V_TAG its log tag, as "[D3D9 HLSL]"
|
||||
* D3D9V_PS_MAKE(d3d, i, src) views.ps[i] and views.ps_reg[i], the
|
||||
* register of its views_c, from @src's
|
||||
* main_fragment at shader model 2; false
|
||||
* if it can't be made
|
||||
* D3D9V_PS_FREE(d3d, i) views.ps[i] let go
|
||||
* D3D9V_PS_BIND(d3d, i) views.ps[i] bound
|
||||
* The macros are undefined again at the end. */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../video_views.h"
|
||||
#include "../video_defines.h"
|
||||
#include "../drivers/d3d_shaders/views_sm2.hlsl.h"
|
||||
|
||||
/* What a frame's placement draws read: the frame's texture, its size,
|
||||
* filter and rotation */
|
||||
struct d3d9_views_draw
|
||||
{
|
||||
const video_frame_info_t *video_info;
|
||||
LPDIRECT3DTEXTURE9 tex;
|
||||
unsigned tex_dims;
|
||||
DWORD filter;
|
||||
unsigned rotation;
|
||||
};
|
||||
|
||||
struct d3d9_views_vertex
|
||||
{
|
||||
float x, y, z, rhw;
|
||||
float u, v;
|
||||
};
|
||||
|
||||
static void D3D9V_FN(views_draw_direct)(d3d9_video_t *d3d,
|
||||
const struct d3d9_views_draw *d, unsigned view,
|
||||
const video_views_rect_t *dst, const video_views_rect_t *cut);
|
||||
static bool D3D9V_FN(views_target_make)(d3d9_video_t *d3d,
|
||||
d3d9_views_target_t *t, unsigned dims);
|
||||
static bool D3D9V_FN(views_target_retire)(d3d9_video_t *d3d,
|
||||
d3d9_views_target_t *t);
|
||||
|
||||
/* The views compositor, with these calls bound in. A preset draws the
|
||||
* frame whole: every view is drawn straight from the frame, its chain
|
||||
* the driver itself. */
|
||||
#define VV_FN(name) D3D9V_FN(vv_##name)
|
||||
#define VV_DRV d3d9_video_t
|
||||
#define VV_CHAIN d3d9_video_t
|
||||
#define VV_CREATE_STOCK(drv) ((void*)(drv))
|
||||
#define VV_SYNC_PARAMS(drv, c) ((void)0)
|
||||
#define VV_DRAW struct d3d9_views_draw
|
||||
#define VV_DIRECT(p, d, v, r, c) D3D9V_FN(views_draw_direct)(p, d, v, r, c)
|
||||
#define VV_PASS(p, d, f, r, c, a) ((void)0)
|
||||
#define VV_TARGET d3d9_views_target_t
|
||||
#define VV_TARGET_MAKE(drv, t, dims) D3D9V_FN(views_target_make)(drv, t, dims)
|
||||
#define VV_TARGET_RETIRE(drv, t) D3D9V_FN(views_target_retire)(drv, t)
|
||||
#include "../video_views_draw.h"
|
||||
|
||||
/* Not with a preset, which draws the frame whole, nor once the shaders
|
||||
* couldn't be made */
|
||||
static bool D3D9V_FN(views_allowed)(const d3d9_video_t *d3d)
|
||||
{
|
||||
return !d3d->views.failed && !(d3d->shader_path && *d3d->shader_path);
|
||||
}
|
||||
|
||||
static void D3D9V_FN(views_publish)(d3d9_video_t *d3d)
|
||||
{
|
||||
retro_atomic_store_release_int(&d3d->views.allowed,
|
||||
D3D9V_FN(views_allowed)(d3d) ? 1 : 0);
|
||||
}
|
||||
|
||||
static bool D3D9V_FN(views_target_retire)(d3d9_video_t *d3d,
|
||||
d3d9_views_target_t *t)
|
||||
{
|
||||
if (t->surf)
|
||||
IDirect3DSurface9_Release(t->surf);
|
||||
if (t->tex)
|
||||
IDirect3DTexture9_Release(t->tex);
|
||||
t->surf = NULL;
|
||||
t->tex = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* A render target of ARGB8 sized @dims */
|
||||
static bool D3D9V_FN(views_target_make)(d3d9_video_t *d3d,
|
||||
d3d9_views_target_t *t, unsigned dims)
|
||||
{
|
||||
if ( SUCCEEDED(IDirect3DDevice9_CreateTexture(d3d->dev,
|
||||
VIDEO_SCALE_W(dims), VIDEO_SCALE_H(dims), 1,
|
||||
D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
|
||||
&t->tex, NULL))
|
||||
&& SUCCEEDED(IDirect3DTexture9_GetSurfaceLevel(t->tex, 0,
|
||||
&t->surf)))
|
||||
return true;
|
||||
RARCH_WARN(D3D9V_TAG " Views can't draw into a %ux%u image. "
|
||||
"Drawing without it.\n", VIDEO_SCALE_W(dims), VIDEO_SCALE_H(dims));
|
||||
D3D9V_FN(views_target_retire)(d3d, t);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Everything views made, let go before the device is reset or freed:
|
||||
* made again on the next frame with views */
|
||||
static void D3D9V_FN(views_release)(d3d9_video_t *d3d)
|
||||
{
|
||||
unsigned i;
|
||||
D3D9V_FN(vv_target_drop)(d3d, &d3d->views.canvas_target,
|
||||
&d3d->views.canvas);
|
||||
D3D9V_FN(vv_target_drop)(d3d, &d3d->views.ui_target, &d3d->views.ui);
|
||||
for (i = 0; i < D3D9_VIEWS_PS_COUNT; i++)
|
||||
{
|
||||
if (d3d->views.ps[i])
|
||||
D3D9V_PS_FREE(d3d, i);
|
||||
d3d->views.ps[i] = NULL;
|
||||
d3d->views.ps_reg[i] = -1;
|
||||
}
|
||||
d3d->views.tried = false;
|
||||
d3d->views.failed = false;
|
||||
d3d->views.blend = false;
|
||||
d3d->views.layer = false;
|
||||
}
|
||||
|
||||
/* The shaders views draw with, made on first use: most sessions never
|
||||
* draw views. Without the rect shader views aren't drawn; without the
|
||||
* others, not into the canvas or UI layer. */
|
||||
static void D3D9V_FN(views_init)(d3d9_video_t *d3d)
|
||||
{
|
||||
D3DCAPS9 caps;
|
||||
unsigned i;
|
||||
|
||||
d3d->views.tried = true;
|
||||
for (i = 0; i < D3D9_VIEWS_PS_COUNT; i++)
|
||||
{
|
||||
d3d->views.ps[i] = NULL;
|
||||
d3d->views.ps_reg[i] = -1;
|
||||
}
|
||||
memset(&caps, 0, sizeof(caps));
|
||||
IDirect3DDevice9_GetDeviceCaps(d3d->dev, &caps);
|
||||
if ( caps.PixelShaderVersion < D3DPS_VERSION(2, 0)
|
||||
|| !D3D9V_PS_MAKE(d3d, D3D9_VIEWS_PS_RECT, d3d9_views_ps_rect)
|
||||
|| d3d->views.ps_reg[D3D9_VIEWS_PS_RECT] < 0)
|
||||
{
|
||||
RARCH_WARN(D3D9V_TAG " Views can't be drawn: no pixel shader "
|
||||
"for them.\n");
|
||||
d3d->views.failed = true;
|
||||
return;
|
||||
}
|
||||
d3d->views.blend = D3D9V_PS_MAKE(d3d, D3D9_VIEWS_PS_BLEND,
|
||||
d3d9_views_ps_blend)
|
||||
&& d3d->views.ps_reg[D3D9_VIEWS_PS_BLEND] >= 0;
|
||||
d3d->views.layer = (caps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND)
|
||||
&& D3D9V_PS_MAKE(d3d, D3D9_VIEWS_PS_LAYER, d3d9_views_ps_layer);
|
||||
}
|
||||
|
||||
/* A scissor rectangle from a top-left one */
|
||||
static void D3D9V_FN(views_cut)(d3d9_video_t *d3d,
|
||||
const video_views_rect_t *r)
|
||||
{
|
||||
RECT rect;
|
||||
rect.left = VIDEO_POS_X(r->pos);
|
||||
rect.top = VIDEO_POS_Y(r->pos);
|
||||
rect.right = rect.left + (LONG)VIDEO_SCALE_W(r->dims);
|
||||
rect.bottom = rect.top + (LONG)VIDEO_SCALE_H(r->dims);
|
||||
IDirect3DDevice9_SetScissorRect(d3d->dev, &rect);
|
||||
}
|
||||
|
||||
/* Texture coordinates @uv, left, top, right and bottom, over @r of the
|
||||
* target, turned @rotation quarter turns as the frame is. Pixel centres
|
||||
* are a half pixel in from a pretransformed quad's edges. */
|
||||
static void D3D9V_FN(views_quad)(d3d9_video_t *d3d,
|
||||
const video_views_rect_t *r, const float *uv, unsigned rotation)
|
||||
{
|
||||
unsigned k;
|
||||
struct d3d9_views_vertex v[4];
|
||||
/* The texture's corners clockwise from its top left, and the quad's
|
||||
* in the same order: top left, top right, bottom right, bottom left */
|
||||
float s[4][2];
|
||||
static const unsigned strip[4] = { 0, 1, 3, 2 };
|
||||
float x0 = (float)VIDEO_POS_X(r->pos) - 0.5f;
|
||||
float y0 = (float)VIDEO_POS_Y(r->pos) - 0.5f;
|
||||
float x1 = x0 + (float)VIDEO_SCALE_W(r->dims);
|
||||
float y1 = y0 + (float)VIDEO_SCALE_H(r->dims);
|
||||
|
||||
s[0][0] = uv[0]; s[0][1] = uv[1];
|
||||
s[1][0] = uv[2]; s[1][1] = uv[1];
|
||||
s[2][0] = uv[2]; s[2][1] = uv[3];
|
||||
s[3][0] = uv[0]; s[3][1] = uv[3];
|
||||
for (k = 0; k < 4; k++)
|
||||
{
|
||||
unsigned c = strip[k];
|
||||
unsigned t = (c + rotation) & 3;
|
||||
v[k].x = (c == 0 || c == 3) ? x0 : x1;
|
||||
v[k].y = (c < 2) ? y0 : y1;
|
||||
v[k].z = 0.0f;
|
||||
v[k].rhw = 1.0f;
|
||||
v[k].u = s[t][0];
|
||||
v[k].v = s[t][1];
|
||||
}
|
||||
IDirect3DDevice9_SetFVF(d3d->dev, D3DFVF_XYZRHW | D3DFVF_TEX1);
|
||||
IDirect3DDevice9_DrawPrimitiveUP(d3d->dev, D3DPT_TRIANGLESTRIP, 2, v,
|
||||
sizeof(v[0]));
|
||||
}
|
||||
|
||||
/* @tex sampled with @filter, clamped */
|
||||
static void D3D9V_FN(views_sample)(d3d9_video_t *d3d,
|
||||
LPDIRECT3DTEXTURE9 tex, DWORD filter)
|
||||
{
|
||||
IDirect3DDevice9_SetTexture(d3d->dev, 0, (IDirect3DBaseTexture9*)tex);
|
||||
IDirect3DDevice9_SetSamplerState(d3d->dev, 0, D3DSAMP_MINFILTER, filter);
|
||||
IDirect3DDevice9_SetSamplerState(d3d->dev, 0, D3DSAMP_MAGFILTER, filter);
|
||||
IDirect3DDevice9_SetSamplerState(d3d->dev, 0, D3DSAMP_ADDRESSU,
|
||||
D3DTADDRESS_CLAMP);
|
||||
IDirect3DDevice9_SetSamplerState(d3d->dev, 0, D3DSAMP_ADDRESSV,
|
||||
D3DTADDRESS_CLAMP);
|
||||
}
|
||||
|
||||
/* View @view over @dst, cut to @cut, straight from the frame */
|
||||
static void D3D9V_FN(views_draw_direct)(d3d9_video_t *d3d,
|
||||
const struct d3d9_views_draw *d, unsigned view,
|
||||
const video_views_rect_t *dst, const video_views_rect_t *cut)
|
||||
{
|
||||
float uv[4], c[4];
|
||||
const struct retro_video_view *v = &d->video_info->views.views[view];
|
||||
float tw = (float)VIDEO_SCALE_W(d->tex_dims);
|
||||
float th = (float)VIDEO_SCALE_H(d->tex_dims);
|
||||
|
||||
uv[0] = (float)v->x / tw;
|
||||
uv[1] = (float)v->y / th;
|
||||
uv[2] = (float)(v->x + v->width) / tw;
|
||||
uv[3] = (float)(v->y + v->height) / th;
|
||||
c[0] = ((float)v->x + 0.5f) / tw;
|
||||
c[1] = ((float)v->y + 0.5f) / th;
|
||||
c[2] = ((float)(v->x + v->width) - 0.5f) / tw;
|
||||
c[3] = ((float)(v->y + v->height) - 0.5f) / th;
|
||||
IDirect3DDevice9_SetPixelShaderConstantF(d3d->dev,
|
||||
d3d->views.ps_reg[D3D9_VIEWS_PS_RECT], c, 1);
|
||||
D3D9V_FN(views_cut)(d3d, cut);
|
||||
D3D9V_FN(views_quad)(d3d, dst, uv, d->rotation);
|
||||
}
|
||||
|
||||
/* Whether this frame draws its views, with each drawn view's chain
|
||||
* picked and the rectangle its first placement takes, and in *blend
|
||||
* whether into the canvas. Drawn whole when the stereo mode or the UI
|
||||
* would need a canvas or layer that can't be had. @menu: the menu is
|
||||
* up. */
|
||||
static bool D3D9V_FN(views_setup)(d3d9_video_t *d3d,
|
||||
video_frame_info_t *video_info, const char *msg, bool menu,
|
||||
bool *blend, d3d9_video_t **chains, video_views_rect_t *rects)
|
||||
{
|
||||
bool views, per_eye, ui;
|
||||
const video_views_layout_t *layout = &video_info->views_layout;
|
||||
bool offscreen = video_info->views.num_views && layout->offscreen;
|
||||
|
||||
if (video_info->views.num_views && !d3d->views.tried)
|
||||
D3D9V_FN(views_init)(d3d);
|
||||
*blend = offscreen && d3d->views.blend
|
||||
&& D3D9V_FN(vv_target_ensure)(d3d, &d3d->views.canvas_target,
|
||||
&d3d->views.canvas, layout->canvas_dims);
|
||||
/* A frame without the blend modes frees the canvas. */
|
||||
if (!offscreen)
|
||||
D3D9V_FN(vv_target_drop)(d3d, &d3d->views.canvas_target,
|
||||
&d3d->views.canvas);
|
||||
per_eye = video_views_ui_per_eye(&video_info->views, layout,
|
||||
D3D9V_FN(views_allowed)(d3d));
|
||||
/* No directive inside the macro's arguments: MSVC's preprocessor
|
||||
* takes it as tokens. */
|
||||
#ifdef HAVE_GFX_WIDGETS
|
||||
ui = VIDEO_VIEWS_UI_WANTED(per_eye, menu, msg,
|
||||
video_info->statistics_show,
|
||||
video_info->widgets_active && gfx_widgets_visible(video_info));
|
||||
#else
|
||||
ui = VIDEO_VIEWS_UI_WANTED(per_eye, menu, msg,
|
||||
video_info->statistics_show, false);
|
||||
#endif
|
||||
views = video_info->views.num_views
|
||||
&& D3D9V_FN(views_allowed)(d3d)
|
||||
&& (!offscreen || *blend)
|
||||
&& (!ui || d3d->views.layer)
|
||||
&& video_views_fit_frame(&video_info->views, d3d->views.frame_dims)
|
||||
&& D3D9V_FN(vv_pick_chains)(d3d, &d3d->views.chains,
|
||||
&video_info->views, layout, true, *blend || !layout->offscreen,
|
||||
*blend ? layout->canvas_dims : video_info->dims, chains, rects);
|
||||
*blend = views && *blend;
|
||||
if (views && !d3d->views.direct_seen)
|
||||
{
|
||||
d3d->views.direct_seen = true;
|
||||
RARCH_LOG(D3D9V_TAG " Views drawn straight from the frame.\n");
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
/* The frame's @height rows, @row_bytes each, @pitch apart at @frame,
|
||||
* into the top left of @tex */
|
||||
static void D3D9V_FN(views_upload)(LPDIRECT3DTEXTURE9 tex,
|
||||
const void *frame, unsigned height, size_t row_bytes, unsigned pitch)
|
||||
{
|
||||
D3DLOCKED_RECT lr;
|
||||
unsigned y;
|
||||
if (FAILED(IDirect3DTexture9_LockRect(tex, 0, &lr, NULL, 0)) || !lr.pBits)
|
||||
return;
|
||||
for (y = 0; y < height; y++)
|
||||
memcpy((uint8_t*)lr.pBits + (size_t)y * lr.Pitch,
|
||||
(const uint8_t*)frame + (size_t)y * pitch, row_bytes);
|
||||
IDirect3DTexture9_UnlockRect(tex, 0);
|
||||
}
|
||||
|
||||
/* A viewport over @dims */
|
||||
static void D3D9V_FN(views_viewport)(d3d9_video_t *d3d, unsigned dims)
|
||||
{
|
||||
D3DVIEWPORT9 vp;
|
||||
vp.X = 0;
|
||||
vp.Y = 0;
|
||||
vp.Width = VIDEO_SCALE_W(dims);
|
||||
vp.Height = VIDEO_SCALE_H(dims);
|
||||
vp.MinZ = 0.0f;
|
||||
vp.MaxZ = 1.0f;
|
||||
IDirect3DDevice9_SetViewport(d3d->dev, &vp);
|
||||
}
|
||||
|
||||
/* Draws the views into the back buffer, or with @blend into the canvas,
|
||||
* whose halves one pass then blends into the back buffer. Leaves no
|
||||
* vertex or pixel shader bound. */
|
||||
static void D3D9V_FN(views_render)(d3d9_video_t *d3d,
|
||||
const struct d3d9_views_draw *d, d3d9_video_t **chains, bool blend)
|
||||
{
|
||||
video_views_rect_t whole;
|
||||
LPDIRECT3DSURFACE9 back = NULL;
|
||||
const video_frame_info_t *info = d->video_info;
|
||||
const video_views_layout_t *layout = &info->views_layout;
|
||||
unsigned dims = blend ? layout->canvas_dims : info->dims;
|
||||
|
||||
IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_ALPHABLENDENABLE, FALSE);
|
||||
IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_ZENABLE, FALSE);
|
||||
IDirect3DDevice9_SetVertexShader(d3d->dev, NULL);
|
||||
if (blend)
|
||||
{
|
||||
IDirect3DDevice9_GetRenderTarget(d3d->dev, 0, &back);
|
||||
IDirect3DDevice9_SetRenderTarget(d3d->dev, 0, d3d->views.canvas.surf);
|
||||
}
|
||||
D3D9V_FN(views_viewport)(d3d, dims);
|
||||
whole.pos = 0;
|
||||
whole.dims = dims;
|
||||
D3D9V_FN(views_cut)(d3d, &whole);
|
||||
/* The back buffer was cleared for the frame. */
|
||||
if (blend)
|
||||
IDirect3DDevice9_Clear(d3d->dev, 0, NULL, D3DCLEAR_TARGET, 0, 1.0f, 0);
|
||||
|
||||
IDirect3DDevice9_BeginScene(d3d->dev);
|
||||
D3D9V_PS_BIND(d3d, D3D9_VIEWS_PS_RECT);
|
||||
D3D9V_FN(views_sample)(d3d, d->tex, d->filter);
|
||||
D3D9V_FN(vv_draw_placements)(d3d, d, layout, chains,
|
||||
blend || !layout->offscreen, dims, true);
|
||||
|
||||
if (blend)
|
||||
{
|
||||
float c[4];
|
||||
float uv[4];
|
||||
c[0] = (layout->stereo_mode == VIDEO_STEREO_MODE_INTERLACED)
|
||||
? 1.0f : 0.0f;
|
||||
c[1] = (float)VIDEO_SCALE_H(layout->canvas_dims);
|
||||
c[2] = 0.0f;
|
||||
c[3] = 0.0f;
|
||||
uv[0] = 0.0f;
|
||||
uv[1] = 0.0f;
|
||||
uv[2] = 1.0f;
|
||||
uv[3] = 1.0f;
|
||||
IDirect3DDevice9_SetRenderTarget(d3d->dev, 0, back);
|
||||
IDirect3DSurface9_Release(back);
|
||||
D3D9V_FN(views_viewport)(d3d, info->dims);
|
||||
whole.dims = info->dims;
|
||||
D3D9V_FN(views_cut)(d3d, &whole);
|
||||
D3D9V_PS_BIND(d3d, D3D9_VIEWS_PS_BLEND);
|
||||
IDirect3DDevice9_SetPixelShaderConstantF(d3d->dev,
|
||||
d3d->views.ps_reg[D3D9_VIEWS_PS_BLEND], c, 1);
|
||||
D3D9V_FN(views_sample)(d3d, d3d->views.canvas.tex, D3DTEXF_POINT);
|
||||
D3D9V_FN(views_quad)(d3d, &whole, uv, 0);
|
||||
}
|
||||
IDirect3DDevice9_EndScene(d3d->dev);
|
||||
whole.dims = info->dims;
|
||||
D3D9V_FN(views_cut)(d3d, &whole);
|
||||
IDirect3DDevice9_SetTexture(d3d->dev, 0, NULL);
|
||||
IDirect3DDevice9_SetPixelShader(d3d->dev, NULL);
|
||||
}
|
||||
|
||||
/* Moves UI drawing into the UI layer, laid out at one eye's size */
|
||||
static void D3D9V_FN(views_ui_begin)(d3d9_video_t *d3d,
|
||||
video_frame_info_t *video_info)
|
||||
{
|
||||
video_views_rect_t whole;
|
||||
unsigned ui_dims = video_info->views_layout.ui_dims;
|
||||
|
||||
IDirect3DDevice9_GetRenderTarget(d3d->dev, 0, &d3d->views.back);
|
||||
IDirect3DDevice9_SetRenderTarget(d3d->dev, 0, d3d->views.ui.surf);
|
||||
whole.pos = 0;
|
||||
whole.dims = ui_dims;
|
||||
D3D9V_FN(views_cut)(d3d, &whole);
|
||||
IDirect3DDevice9_Clear(d3d->dev, 0, NULL, D3DCLEAR_TARGET, 0, 1.0f, 0);
|
||||
/* Menus and widgets size from video_info->dims, fonts from
|
||||
* vp.full_dims. */
|
||||
video_views_ui_enter(&d3d->views.saved, &video_info->dims,
|
||||
&d3d->vp.full_dims, ui_dims);
|
||||
/* Drawn over transparent black, the layer's colour premultiplied */
|
||||
IDirect3DDevice9_SetRenderState(d3d->dev,
|
||||
D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
|
||||
IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_SRCBLENDALPHA,
|
||||
D3DBLEND_ONE);
|
||||
IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_DESTBLENDALPHA,
|
||||
D3DBLEND_INVSRCALPHA);
|
||||
}
|
||||
|
||||
/* The out viewport is the whole window while views draw, so a menu not
|
||||
* drawn full screen is fitted to its target, of @dims, here. */
|
||||
static void D3D9V_FN(views_menu_viewport)(d3d9_video_t *d3d, unsigned dims)
|
||||
{
|
||||
struct video_viewport vp;
|
||||
D3DVIEWPORT9 out;
|
||||
memset(&vp, 0, sizeof(vp));
|
||||
vp.full_dims = dims;
|
||||
video_driver_update_viewport(&vp, false, d3d->keep_aspect, true);
|
||||
out.X = (VIDEO_POS_X(vp.pos) > 0) ? VIDEO_POS_X(vp.pos) : 0;
|
||||
out.Y = (VIDEO_POS_Y(vp.pos) > 0) ? VIDEO_POS_Y(vp.pos) : 0;
|
||||
out.Width = VIDEO_SCALE_W(vp.dims);
|
||||
out.Height = VIDEO_SCALE_H(vp.dims);
|
||||
out.MinZ = 0.0f;
|
||||
out.MaxZ = 1.0f;
|
||||
IDirect3DDevice9_SetViewport(d3d->dev, &out);
|
||||
}
|
||||
|
||||
/* Back onto the back buffer, and the UI layer into each eye's area */
|
||||
static void D3D9V_FN(views_ui_end)(d3d9_video_t *d3d,
|
||||
video_frame_info_t *video_info)
|
||||
{
|
||||
unsigned a;
|
||||
video_views_rect_t whole;
|
||||
float uv[4];
|
||||
const video_views_layout_t *layout = &video_info->views_layout;
|
||||
/* Only full side by side places it unscaled. */
|
||||
DWORD filter = (layout->stereo_mode == VIDEO_STEREO_MODE_SBS_FULL)
|
||||
? D3DTEXF_POINT : D3DTEXF_LINEAR;
|
||||
|
||||
IDirect3DDevice9_SetRenderState(d3d->dev,
|
||||
D3DRS_SEPARATEALPHABLENDENABLE, FALSE);
|
||||
video_views_ui_leave(&d3d->views.saved, &video_info->dims,
|
||||
&d3d->vp.full_dims);
|
||||
IDirect3DDevice9_SetRenderTarget(d3d->dev, 0, d3d->views.back);
|
||||
IDirect3DSurface9_Release(d3d->views.back);
|
||||
d3d->views.back = NULL;
|
||||
|
||||
uv[0] = 0.0f;
|
||||
uv[1] = 0.0f;
|
||||
uv[2] = 1.0f;
|
||||
uv[3] = 1.0f;
|
||||
D3D9V_FN(views_viewport)(d3d, video_info->dims);
|
||||
IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_ZENABLE, FALSE);
|
||||
IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_ALPHABLENDENABLE, TRUE);
|
||||
IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_SRCBLEND, D3DBLEND_ONE);
|
||||
IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_DESTBLEND,
|
||||
D3DBLEND_INVSRCALPHA);
|
||||
IDirect3DDevice9_SetVertexShader(d3d->dev, NULL);
|
||||
D3D9V_PS_BIND(d3d, D3D9_VIEWS_PS_LAYER);
|
||||
D3D9V_FN(views_sample)(d3d, d3d->views.ui.tex, filter);
|
||||
IDirect3DDevice9_BeginScene(d3d->dev);
|
||||
for (a = 0; a < layout->num_areas; a++)
|
||||
{
|
||||
D3D9V_FN(views_cut)(d3d, &layout->areas[a]);
|
||||
D3D9V_FN(views_quad)(d3d, &layout->areas[a], uv, 0);
|
||||
}
|
||||
IDirect3DDevice9_EndScene(d3d->dev);
|
||||
IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_SRCBLEND,
|
||||
D3DBLEND_SRCALPHA);
|
||||
IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_ALPHABLENDENABLE, FALSE);
|
||||
whole.pos = 0;
|
||||
whole.dims = video_info->dims;
|
||||
D3D9V_FN(views_cut)(d3d, &whole);
|
||||
IDirect3DDevice9_SetTexture(d3d->dev, 0, NULL);
|
||||
IDirect3DDevice9_SetPixelShader(d3d->dev, NULL);
|
||||
}
|
||||
|
||||
static void D3D9V_FN(set_view_count)(void *data, unsigned count)
|
||||
{
|
||||
unsigned i;
|
||||
d3d9_video_t *d3d = (d3d9_video_t*)data;
|
||||
if (!d3d)
|
||||
return;
|
||||
d3d->views.chains.count = MIN(count, RETRO_VIDEO_VIEWS_MAX);
|
||||
for (i = d3d->views.chains.count; i < RETRO_VIDEO_VIEWS_MAX; i++)
|
||||
d3d->views.chains.stock[i] = NULL;
|
||||
}
|
||||
|
||||
#undef D3D9V_FN
|
||||
#undef D3D9V_TAG
|
||||
#undef D3D9V_PS_MAKE
|
||||
#undef D3D9V_PS_FREE
|
||||
#undef D3D9V_PS_BIND
|
||||
+212
-17
@@ -327,19 +327,9 @@ static INLINE void d3d9_recompute_pass_sizes(
|
||||
unsigned out_width = 0;
|
||||
unsigned out_height = 0;
|
||||
|
||||
/* The first pass's texture is the frame's, which
|
||||
* d3d9_cg_renderchain_fit_frame sizes. */
|
||||
link_info.pass = &d3d->shader.pass[0];
|
||||
link_info.tex_dims = VIDEO_SCALE_PACK(current_width,
|
||||
current_height);
|
||||
|
||||
if (!d3d9_cg_renderchain_set_pass_size(dev,
|
||||
(struct shader_pass*)&chain->passes->data[0],
|
||||
(struct shader_pass*)&chain->passes->data[
|
||||
chain->passes->count - 1],
|
||||
link_info.tex_dims))
|
||||
{
|
||||
RARCH_ERR("[D3D9] Failed to set pass size.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 1; i < (int) d3d->shader.passes; i++)
|
||||
{
|
||||
@@ -388,6 +378,39 @@ static INLINE void d3d9_recompute_pass_sizes(
|
||||
}
|
||||
}
|
||||
|
||||
/* The frame's textures grown to hold a frame of @width x @height, past
|
||||
* the size the core declared; false if they couldn't be, and the frame
|
||||
* is cut to them. */
|
||||
static bool d3d9_cg_renderchain_fit_frame(d3d9_cg_renderchain_t *chain,
|
||||
unsigned width, unsigned height)
|
||||
{
|
||||
unsigned i, tw, th;
|
||||
D3DSURFACE_DESC desc;
|
||||
struct shader_pass *first = (struct shader_pass*)&chain->passes->data[0];
|
||||
tw = VIDEO_SCALE_W(first->info.tex_dims);
|
||||
th = VIDEO_SCALE_H(first->info.tex_dims);
|
||||
if (width <= tw && height <= th)
|
||||
return true;
|
||||
if (FAILED(IDirect3DTexture9_GetLevelDesc(chain->prev.tex[0], 0, &desc)))
|
||||
return false;
|
||||
tw = next_pow2(MAX(width, tw));
|
||||
th = next_pow2(MAX(height, th));
|
||||
for (i = 0; i < TEXTURES; i++)
|
||||
{
|
||||
LPDIRECT3DTEXTURE9 tex = NULL;
|
||||
if (FAILED(IDirect3DDevice9_CreateTexture(chain->dev, tw, th, 1, 0,
|
||||
desc.Format, D3DPOOL_MANAGED, &tex, NULL)))
|
||||
return false;
|
||||
IDirect3DTexture9_Release(chain->prev.tex[i]);
|
||||
chain->prev.tex[i] = tex;
|
||||
chain->prev.last_dims[i] = 0;
|
||||
}
|
||||
first->tex = chain->prev.tex[chain->prev.ptr];
|
||||
first->last_dims = 0;
|
||||
first->info.tex_dims = VIDEO_SCALE_PACK(tw, th);
|
||||
return true;
|
||||
}
|
||||
|
||||
#define DECL_FVF_COLOR(stream, offset, index) \
|
||||
{ (WORD)(stream), (WORD)(offset * sizeof(float)), D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, \
|
||||
D3DDECLUSAGE_COLOR, (BYTE)(index) } \
|
||||
@@ -3226,13 +3249,58 @@ static void d3d9_cg_renderchain_render(
|
||||
chain->out_vp->Height, 0);
|
||||
}
|
||||
|
||||
#ifdef D3D9_VIEWS
|
||||
/* Views pixel shader @i from @src in the chain's context, and its
|
||||
* constant's register */
|
||||
static bool d3d9_cg_views_ps_make(d3d9_video_t *d3d, unsigned i,
|
||||
const char *src)
|
||||
{
|
||||
CGprogram prog;
|
||||
CGparameter param;
|
||||
cg_renderchain_t *chain = (cg_renderchain_t*)d3d->renderchain_data;
|
||||
if (!chain)
|
||||
return false;
|
||||
cgGetError();
|
||||
if (!(prog = cgCreateProgram(chain->cgCtx, CG_SOURCE, src,
|
||||
CG_PROFILE_PS_2_0, "main_fragment", NULL)))
|
||||
return false;
|
||||
cgD3D9LoadProgram(prog, CG_FALSE, 0);
|
||||
if (cgGetError() != CG_NO_ERROR)
|
||||
{
|
||||
cgDestroyProgram(prog);
|
||||
return false;
|
||||
}
|
||||
param = cgGetNamedParameter(prog, "views_c");
|
||||
d3d->views.ps[i] = prog;
|
||||
d3d->views.ps_reg[i] = param
|
||||
? (int)cgGetParameterResourceIndex(param) : -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
#define D3D9V_FN(name) d3d9_cg_##name
|
||||
#define D3D9V_TAG "[D3D9 Cg]"
|
||||
#define D3D9V_PS_MAKE(d, i, src) d3d9_cg_views_ps_make(d, i, src)
|
||||
#define D3D9V_PS_FREE(d, i) cgDestroyProgram((CGprogram)(d)->views.ps[i])
|
||||
#define D3D9V_PS_BIND(d, i) cgD3D9BindProgram((CGprogram)(d)->views.ps[i])
|
||||
#include "../common/d3d9_views.h"
|
||||
#endif
|
||||
|
||||
static uint32_t d3d9_cg_get_flags(void *data)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
#ifdef D3D9_VIEWS
|
||||
d3d9_video_t *d3d = (d3d9_video_t*)data;
|
||||
#endif
|
||||
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION);
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING);
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_CG);
|
||||
#ifdef D3D9_VIEWS
|
||||
if (d3d && retro_atomic_load_acquire_int(&d3d->views.allowed))
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_VIDEO_VIEWS);
|
||||
if (d3d && retro_atomic_load_acquire_int(&d3d->views.fallback))
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_VIDEO_VIEWS_FALLBACK);
|
||||
#endif
|
||||
|
||||
return flags;
|
||||
}
|
||||
@@ -3251,7 +3319,11 @@ static void d3d9_cg_deinitialize(d3d9_video_t *d3d)
|
||||
{
|
||||
if (!d3d)
|
||||
return;
|
||||
|
||||
#ifdef D3D9_VIEWS
|
||||
/* Its targets are in the default pool, which a reset drops, and its
|
||||
* programs in the chain's context. */
|
||||
d3d9_cg_views_release(d3d);
|
||||
#endif
|
||||
|
||||
d3d9_cg_deinit_chain(d3d);
|
||||
d3d9_vertex_buffer_free(d3d->menu_display.buffer,
|
||||
@@ -3668,7 +3740,13 @@ static bool d3d9_cg_initialize(d3d9_video_t *d3d, const video_info_t *info)
|
||||
/* d3d->vp.full_* was written by the caller (d3d9_cg_init_internal
|
||||
* has already called set_size at this point). */
|
||||
d3d9_cg_set_viewport(d3d,
|
||||
d3d->vp.full_dims, false, true);
|
||||
d3d->vp.full_dims,
|
||||
#ifdef D3D9_VIEWS
|
||||
d3d->views.active,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
true);
|
||||
|
||||
|
||||
{
|
||||
@@ -3986,6 +4064,9 @@ static void *d3d9_cg_init(const video_info_t *info)
|
||||
}
|
||||
|
||||
d3d->keep_aspect = info->force_aspect;
|
||||
#ifdef D3D9_VIEWS
|
||||
d3d9_cg_views_publish(d3d);
|
||||
#endif
|
||||
|
||||
return d3d;
|
||||
}
|
||||
@@ -4403,6 +4484,14 @@ static bool d3d9_cg_frame(void *data, const void *frame,
|
||||
#ifdef HAVE_GFX_WIDGETS
|
||||
bool widgets_active = video_info->widgets_active;
|
||||
#endif
|
||||
bool views_per_eye = false;
|
||||
#ifdef D3D9_VIEWS
|
||||
d3d9_video_t *view_chains[RETRO_VIDEO_VIEWS_MAX];
|
||||
video_views_rect_t view_rects[RETRO_VIDEO_VIEWS_MAX];
|
||||
bool views = false;
|
||||
bool views_blend = false;
|
||||
bool views_ui = false;
|
||||
#endif
|
||||
|
||||
if (!frame)
|
||||
return true;
|
||||
@@ -4422,12 +4511,46 @@ static bool d3d9_cg_frame(void *data, const void *frame,
|
||||
}
|
||||
}
|
||||
|
||||
/* A frame past the size the core declared */
|
||||
{
|
||||
cg_renderchain_t *_chain = (cg_renderchain_t*)d3d->renderchain_data;
|
||||
if (!d3d9_cg_renderchain_fit_frame(&_chain->chain,
|
||||
frame_width, frame_height))
|
||||
{
|
||||
unsigned tex_dims = _chain->chain.passes->data[0].info.tex_dims;
|
||||
frame_width = MIN(frame_width, VIDEO_SCALE_W(tex_dims));
|
||||
frame_height = MIN(frame_height, VIDEO_SCALE_H(tex_dims));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef D3D9_VIEWS
|
||||
d3d->views.frame_dims = VIDEO_SCALE_PACK(frame_width, frame_height);
|
||||
views = d3d9_cg_views_setup(d3d, video_info, msg,
|
||||
d3d->menu && d3d->menu->enabled, &views_blend, view_chains,
|
||||
view_rects);
|
||||
/* The viewport is the whole window while views draw. */
|
||||
if (views != d3d->views.active)
|
||||
{
|
||||
d3d->views.active = views;
|
||||
d3d->should_resize = true;
|
||||
}
|
||||
retro_atomic_store_release_int(&d3d->views.fallback,
|
||||
(video_info->views.num_views && !views) ? 1 : 0);
|
||||
d3d9_cg_views_publish(d3d);
|
||||
#endif
|
||||
|
||||
if (d3d->should_resize)
|
||||
{
|
||||
cg_renderchain_t *_chain = (cg_renderchain_t*)
|
||||
d3d->renderchain_data;
|
||||
d3d9_cg_renderchain_t *chain = (d3d9_cg_renderchain_t*)&_chain->chain;
|
||||
d3d9_cg_set_viewport(d3d, VIDEO_SCALE_PACK(width, height), false, true);
|
||||
d3d9_cg_set_viewport(d3d, VIDEO_SCALE_PACK(width, height),
|
||||
#ifdef D3D9_VIEWS
|
||||
d3d->views.active,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
true);
|
||||
|
||||
if (chain)
|
||||
chain->out_vp = (D3DVIEWPORT9*)&d3d->out_vp;
|
||||
@@ -4461,6 +4584,26 @@ static bool d3d9_cg_frame(void *data, const void *frame,
|
||||
CG_DEBUG_CHECK(_chain->cgCtx, "set_mvp_transpose SetUniformMatrix");
|
||||
}
|
||||
}
|
||||
#ifdef D3D9_VIEWS
|
||||
/* The frame into its texture as ever, each view drawn from there */
|
||||
if (views)
|
||||
{
|
||||
struct d3d9_views_draw d;
|
||||
cg_renderchain_t *_chain = (cg_renderchain_t*)d3d->renderchain_data;
|
||||
struct shader_pass *first = &_chain->chain.passes->data[0];
|
||||
d.video_info = video_info;
|
||||
d.tex = _chain->chain.prev.tex[_chain->chain.prev.ptr];
|
||||
d.tex_dims = first->info.tex_dims;
|
||||
d.filter = (DWORD)d3d_translate_filter(first->info.pass->filter);
|
||||
d.rotation = d3d->dev_rotation;
|
||||
d3d9_cg_views_upload(d.tex, frame, frame_height,
|
||||
(size_t)frame_width * _chain->chain.pixel_size, pitch);
|
||||
d3d9_cg_views_render(d3d, &d, view_chains, views_blend);
|
||||
cgD3D9BindProgram((CGprogram)_chain->stock_shader.fprg);
|
||||
cgD3D9BindProgram((CGprogram)_chain->stock_shader.vprg);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
d3d9_cg_renderchain_render(
|
||||
d3d, frame, frame_width, frame_height,
|
||||
pitch, d3d->dev_rotation);
|
||||
@@ -4479,8 +4622,39 @@ static bool d3d9_cg_frame(void *data, const void *frame,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef D3D9_VIEWS
|
||||
/* As the main thread laid the UI out, also in a frame drawn whole:
|
||||
* it lays out later frames whole once it reads the fallback. */
|
||||
views_per_eye = video_views_ui_per_eye(&video_info->views,
|
||||
&video_info->views_layout, d3d9_cg_views_allowed(d3d));
|
||||
if (!views_per_eye)
|
||||
d3d9_cg_vv_target_drop(d3d, &d3d->views.ui_target, &d3d->views.ui);
|
||||
/* Drawn and composited only when there is UI; overlays are hidden.
|
||||
* No directive inside the macro's arguments: MSVC's preprocessor
|
||||
* takes it as tokens. */
|
||||
#ifdef HAVE_GFX_WIDGETS
|
||||
views_ui = VIDEO_VIEWS_UI_WANTED(views_per_eye,
|
||||
d3d->menu->enabled, msg, statistics_show,
|
||||
widgets_active && gfx_widgets_visible(video_info))
|
||||
#else
|
||||
views_ui = VIDEO_VIEWS_UI_WANTED(views_per_eye,
|
||||
d3d->menu->enabled, msg, statistics_show, false)
|
||||
#endif
|
||||
&& d3d->views.layer
|
||||
&& d3d9_cg_vv_target_ensure(d3d, &d3d->views.ui_target,
|
||||
&d3d->views.ui, video_info->views_layout.ui_dims);
|
||||
if (views_ui)
|
||||
{
|
||||
d3d9_cg_views_ui_begin(d3d, video_info);
|
||||
width = VIDEO_SCALE_W(video_info->dims);
|
||||
height = VIDEO_SCALE_H(video_info->dims);
|
||||
screen_vp.Width = width;
|
||||
screen_vp.Height = height;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OVERLAY
|
||||
if (d3d->overlays_enabled && overlay_behind_menu)
|
||||
if (d3d->overlays_enabled && overlay_behind_menu && !views_per_eye)
|
||||
{
|
||||
for (i = 0; i < d3d->overlays_size; i++)
|
||||
d3d9_cg_overlay_render(d3d, width, height, &d3d->overlays[i], true);
|
||||
@@ -4490,6 +4664,10 @@ static bool d3d9_cg_frame(void *data, const void *frame,
|
||||
#ifdef HAVE_MENU
|
||||
if (d3d->menu && d3d->menu->enabled)
|
||||
{
|
||||
#ifdef D3D9_VIEWS
|
||||
if (d3d->views.active || views_ui)
|
||||
d3d9_cg_views_menu_viewport(d3d, video_info->dims);
|
||||
#endif
|
||||
d3d9_cg_overlay_render(d3d, width, height, d3d->menu, false);
|
||||
|
||||
d3d->menu_display.offset = 0;
|
||||
@@ -4525,7 +4703,7 @@ static bool d3d9_cg_frame(void *data, const void *frame,
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OVERLAY
|
||||
if (d3d->overlays_enabled && !overlay_behind_menu)
|
||||
if (d3d->overlays_enabled && !overlay_behind_menu && !views_per_eye)
|
||||
{
|
||||
for (i = 0; i < d3d->overlays_size; i++)
|
||||
d3d9_cg_overlay_render(d3d, width, height, &d3d->overlays[i], true);
|
||||
@@ -4583,6 +4761,16 @@ static bool d3d9_cg_frame(void *data, const void *frame,
|
||||
IDirect3DDevice9_EndScene(d3d->dev);
|
||||
}
|
||||
|
||||
#ifdef D3D9_VIEWS
|
||||
if (views_ui)
|
||||
{
|
||||
cg_renderchain_t *_chain = (cg_renderchain_t*)d3d->renderchain_data;
|
||||
d3d9_cg_views_ui_end(d3d, video_info);
|
||||
cgD3D9BindProgram((CGprogram)_chain->stock_shader.fprg);
|
||||
cgD3D9BindProgram((CGprogram)_chain->stock_shader.vprg);
|
||||
}
|
||||
#endif
|
||||
|
||||
video_driver_update_title(NULL);
|
||||
|
||||
{
|
||||
@@ -4809,6 +4997,13 @@ static const video_poke_interface_t d3d9_cg_poke_interface = {
|
||||
NULL, /* hw_ring_framebuffer */
|
||||
d3d9_cg_update_texture,
|
||||
d3d9_cg_get_swap_interval_cap
|
||||
#ifdef D3D9_VIEWS
|
||||
,
|
||||
NULL, /* texture_lend */
|
||||
NULL, /* texture_lend_ready */
|
||||
NULL, /* get_last_present_wait */
|
||||
d3d9_cg_set_view_count
|
||||
#endif
|
||||
};
|
||||
|
||||
static void d3d9_cg_get_poke_interface(void *data,
|
||||
|
||||
+213
-16
@@ -313,19 +313,9 @@ static INLINE void d3d9_recompute_pass_sizes(
|
||||
unsigned out_width = 0;
|
||||
unsigned out_height = 0;
|
||||
|
||||
/* The first pass's texture is the frame's, which
|
||||
* d3d9_hlsl_renderchain_fit_frame sizes. */
|
||||
link_info.pass = &d3d->shader.pass[0];
|
||||
link_info.tex_dims = VIDEO_SCALE_PACK(current_width,
|
||||
current_height);
|
||||
|
||||
if (!d3d9_hlsl_renderchain_set_pass_size(dev,
|
||||
(struct shader_pass*)&chain->passes->data[0],
|
||||
(struct shader_pass*)&chain->passes->data[
|
||||
chain->passes->count - 1],
|
||||
link_info.tex_dims))
|
||||
{
|
||||
RARCH_ERR("[D3D9] Failed to set pass size.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 1; i < (int) d3d->shader.passes; i++)
|
||||
{
|
||||
@@ -374,6 +364,39 @@ static INLINE void d3d9_recompute_pass_sizes(
|
||||
}
|
||||
}
|
||||
|
||||
/* The frame's textures grown to hold a frame of @width x @height, past
|
||||
* the size the core declared; false if they couldn't be, and the frame
|
||||
* is cut to them. */
|
||||
static bool d3d9_hlsl_renderchain_fit_frame(d3d9_hlsl_renderchain_t *chain,
|
||||
unsigned width, unsigned height)
|
||||
{
|
||||
unsigned i, tw, th;
|
||||
D3DSURFACE_DESC desc;
|
||||
struct shader_pass *first = (struct shader_pass*)&chain->passes->data[0];
|
||||
tw = VIDEO_SCALE_W(first->info.tex_dims);
|
||||
th = VIDEO_SCALE_H(first->info.tex_dims);
|
||||
if (width <= tw && height <= th)
|
||||
return true;
|
||||
if (FAILED(IDirect3DTexture9_GetLevelDesc(chain->prev.tex[0], 0, &desc)))
|
||||
return false;
|
||||
tw = next_pow2(MAX(width, tw));
|
||||
th = next_pow2(MAX(height, th));
|
||||
for (i = 0; i < TEXTURES; i++)
|
||||
{
|
||||
LPDIRECT3DTEXTURE9 tex = NULL;
|
||||
if (FAILED(IDirect3DDevice9_CreateTexture(chain->dev, tw, th, 1, 0,
|
||||
desc.Format, D3DPOOL_MANAGED, &tex, NULL)))
|
||||
return false;
|
||||
IDirect3DTexture9_Release(chain->prev.tex[i]);
|
||||
chain->prev.tex[i] = tex;
|
||||
chain->prev.last_dims[i] = 0;
|
||||
}
|
||||
first->tex = chain->prev.tex[chain->prev.ptr];
|
||||
first->last_dims = 0;
|
||||
first->info.tex_dims = VIDEO_SCALE_PACK(tw, th);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
* SM3.0 CTAB (Constant Table) parser
|
||||
*
|
||||
@@ -6605,20 +6628,64 @@ static bool d3d9_hlsl_load_program_from_file_ex(
|
||||
|
||||
/* END HLSL RENDERCHAIN */
|
||||
|
||||
#ifdef D3D9_VIEWS
|
||||
/* Views pixel shader @i from @src, and its constant's register */
|
||||
static bool d3d9_hlsl_views_ps_make(d3d9_video_t *d3d, unsigned i,
|
||||
const char *src)
|
||||
{
|
||||
bool ok;
|
||||
D3DBlob code = NULL;
|
||||
if (!d3d_compile(src, strlen(src), "views", "main_fragment", "ps_2_0",
|
||||
&code))
|
||||
return false;
|
||||
ok = SUCCEEDED(IDirect3DDevice9_CreatePixelShader(d3d->dev,
|
||||
(const DWORD*)code->lpVtbl->GetBufferPointer(code),
|
||||
(LPDIRECT3DPIXELSHADER9*)&d3d->views.ps[i]));
|
||||
d3d->views.ps_reg[i] = d3d9_hlsl_ctab_find_register(
|
||||
(const DWORD*)code->lpVtbl->GetBufferPointer(code),
|
||||
code->lpVtbl->GetBufferSize(code) / sizeof(DWORD), "views_c",
|
||||
NULL, NULL);
|
||||
code->lpVtbl->Release(code);
|
||||
return ok;
|
||||
}
|
||||
|
||||
#define D3D9V_FN(name) d3d9_hlsl_##name
|
||||
#define D3D9V_TAG "[D3D9 HLSL]"
|
||||
#define D3D9V_PS_MAKE(d, i, src) d3d9_hlsl_views_ps_make(d, i, src)
|
||||
#define D3D9V_PS_FREE(d, i) IDirect3DPixelShader9_Release( \
|
||||
(LPDIRECT3DPIXELSHADER9)(d)->views.ps[i])
|
||||
#define D3D9V_PS_BIND(d, i) IDirect3DDevice9_SetPixelShader((d)->dev, \
|
||||
(LPDIRECT3DPIXELSHADER9)(d)->views.ps[i])
|
||||
#include "../common/d3d9_views.h"
|
||||
#endif
|
||||
|
||||
static uint32_t d3d9_hlsl_get_flags(void *data)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
#ifdef D3D9_VIEWS
|
||||
d3d9_video_t *d3d = (d3d9_video_t*)data;
|
||||
#endif
|
||||
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION);
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING);
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_HLSL);
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_CG);
|
||||
#ifdef D3D9_VIEWS
|
||||
if (d3d && retro_atomic_load_acquire_int(&d3d->views.allowed))
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_VIDEO_VIEWS);
|
||||
if (d3d && retro_atomic_load_acquire_int(&d3d->views.fallback))
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_VIDEO_VIEWS_FALLBACK);
|
||||
#endif
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void d3d9_hlsl_deinitialize(d3d9_video_t *d3d)
|
||||
{
|
||||
#ifdef D3D9_VIEWS
|
||||
/* Its targets are in the default pool, which a reset drops. */
|
||||
d3d9_hlsl_views_release(d3d);
|
||||
#endif
|
||||
|
||||
hlsl_d3d9_renderchain_free(d3d->renderchain_data);
|
||||
|
||||
@@ -7051,7 +7118,13 @@ static bool d3d9_hlsl_initialize(
|
||||
/* d3d->vp.full_* was written by the caller (d3d9_hlsl_init_internal
|
||||
* has already called set_size at this point). */
|
||||
d3d9_hlsl_set_viewport(d3d,
|
||||
d3d->vp.full_dims, false, true);
|
||||
d3d->vp.full_dims,
|
||||
#ifdef D3D9_VIEWS
|
||||
d3d->views.active,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
true);
|
||||
|
||||
|
||||
{
|
||||
@@ -7320,6 +7393,9 @@ static void *d3d9_hlsl_init(const video_info_t *info)
|
||||
goto error;
|
||||
|
||||
d3d->keep_aspect = info->force_aspect;
|
||||
#ifdef D3D9_VIEWS
|
||||
d3d9_hlsl_views_publish(d3d);
|
||||
#endif
|
||||
|
||||
return d3d;
|
||||
|
||||
@@ -7769,6 +7845,14 @@ static bool d3d9_hlsl_frame(void *data, const void *frame,
|
||||
#ifdef HAVE_GFX_WIDGETS
|
||||
bool widgets_active = video_info->widgets_active;
|
||||
#endif
|
||||
bool views_per_eye = false;
|
||||
#ifdef D3D9_VIEWS
|
||||
d3d9_video_t *view_chains[RETRO_VIDEO_VIEWS_MAX];
|
||||
video_views_rect_t view_rects[RETRO_VIDEO_VIEWS_MAX];
|
||||
bool views = false;
|
||||
bool views_blend = false;
|
||||
bool views_ui = false;
|
||||
#endif
|
||||
|
||||
if (!frame)
|
||||
return true;
|
||||
@@ -7790,12 +7874,46 @@ static bool d3d9_hlsl_frame(void *data, const void *frame,
|
||||
}
|
||||
}
|
||||
|
||||
/* A frame past the size the core declared */
|
||||
{
|
||||
hlsl_renderchain_t *_chain = (hlsl_renderchain_t*)d3d->renderchain_data;
|
||||
if (!d3d9_hlsl_renderchain_fit_frame(&_chain->chain,
|
||||
frame_width, frame_height))
|
||||
{
|
||||
unsigned tex_dims = _chain->chain.passes->data[0].info.tex_dims;
|
||||
frame_width = MIN(frame_width, VIDEO_SCALE_W(tex_dims));
|
||||
frame_height = MIN(frame_height, VIDEO_SCALE_H(tex_dims));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef D3D9_VIEWS
|
||||
d3d->views.frame_dims = VIDEO_SCALE_PACK(frame_width, frame_height);
|
||||
views = d3d9_hlsl_views_setup(d3d, video_info, msg,
|
||||
d3d->menu && d3d->menu->enabled, &views_blend, view_chains,
|
||||
view_rects);
|
||||
/* The viewport is the whole window while views draw. */
|
||||
if (views != d3d->views.active)
|
||||
{
|
||||
d3d->views.active = views;
|
||||
d3d->should_resize = true;
|
||||
}
|
||||
retro_atomic_store_release_int(&d3d->views.fallback,
|
||||
(video_info->views.num_views && !views) ? 1 : 0);
|
||||
d3d9_hlsl_views_publish(d3d);
|
||||
#endif
|
||||
|
||||
if (d3d->should_resize)
|
||||
{
|
||||
hlsl_renderchain_t *_chain = (hlsl_renderchain_t*)d3d->renderchain_data;
|
||||
d3d9_hlsl_renderchain_t *chain = (d3d9_hlsl_renderchain_t*)&_chain->chain;
|
||||
|
||||
d3d9_hlsl_set_viewport(d3d, VIDEO_SCALE_PACK(width, height), false, true);
|
||||
d3d9_hlsl_set_viewport(d3d, VIDEO_SCALE_PACK(width, height),
|
||||
#ifdef D3D9_VIEWS
|
||||
d3d->views.active,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
true);
|
||||
|
||||
if (chain)
|
||||
chain->out_vp = (D3DVIEWPORT9*)&d3d->out_vp;
|
||||
@@ -7819,6 +7937,31 @@ static bool d3d9_hlsl_frame(void *data, const void *frame,
|
||||
|
||||
IDirect3DDevice9_SetVertexShaderConstantF(d3d->dev, 0,
|
||||
(const float*)&d3d->mvp, 4);
|
||||
#ifdef D3D9_VIEWS
|
||||
/* The frame into its texture as ever, each view drawn from there */
|
||||
if (views)
|
||||
{
|
||||
struct d3d9_views_draw d;
|
||||
hlsl_renderchain_t *_chain = (hlsl_renderchain_t*)d3d->renderchain_data;
|
||||
struct shader_pass *first = &_chain->chain.passes->data[0];
|
||||
d.video_info = video_info;
|
||||
d.tex = _chain->chain.prev.tex[_chain->chain.prev.ptr];
|
||||
d.tex_dims = first->info.tex_dims;
|
||||
d.filter = (DWORD)d3d_translate_filter(first->info.pass->filter);
|
||||
d.rotation = d3d->dev_rotation;
|
||||
d3d9_hlsl_views_upload(d.tex, frame, frame_height,
|
||||
(size_t)frame_width * _chain->chain.pixel_size, pitch);
|
||||
d3d9_hlsl_views_render(d3d, &d, view_chains, views_blend);
|
||||
IDirect3DDevice9_SetVertexShader(d3d->dev,
|
||||
(LPDIRECT3DVERTEXSHADER9)_chain->stock_shader.vprg);
|
||||
IDirect3DDevice9_SetPixelShader(d3d->dev,
|
||||
(LPDIRECT3DPIXELSHADER9)_chain->stock_shader.fprg);
|
||||
if (_chain->stock_data.vs_map.mvp >= 0)
|
||||
IDirect3DDevice9_SetVertexShaderConstantF(d3d->dev,
|
||||
_chain->stock_data.vs_map.mvp, (const float*)&d3d->mvp, 4);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
hlsl_d3d9_renderchain_render(
|
||||
d3d, frame, frame_width, frame_height,
|
||||
pitch, d3d->dev_rotation);
|
||||
@@ -7842,8 +7985,39 @@ static bool d3d9_hlsl_frame(void *data, const void *frame,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef D3D9_VIEWS
|
||||
/* As the main thread laid the UI out, also in a frame drawn whole:
|
||||
* it lays out later frames whole once it reads the fallback. */
|
||||
views_per_eye = video_views_ui_per_eye(&video_info->views,
|
||||
&video_info->views_layout, d3d9_hlsl_views_allowed(d3d));
|
||||
if (!views_per_eye)
|
||||
d3d9_hlsl_vv_target_drop(d3d, &d3d->views.ui_target, &d3d->views.ui);
|
||||
/* Drawn and composited only when there is UI; overlays are hidden.
|
||||
* No directive inside the macro's arguments: MSVC's preprocessor
|
||||
* takes it as tokens. */
|
||||
#ifdef HAVE_GFX_WIDGETS
|
||||
views_ui = VIDEO_VIEWS_UI_WANTED(views_per_eye,
|
||||
d3d->menu->enabled, msg, statistics_show,
|
||||
widgets_active && gfx_widgets_visible(video_info))
|
||||
#else
|
||||
views_ui = VIDEO_VIEWS_UI_WANTED(views_per_eye,
|
||||
d3d->menu->enabled, msg, statistics_show, false)
|
||||
#endif
|
||||
&& d3d->views.layer
|
||||
&& d3d9_hlsl_vv_target_ensure(d3d, &d3d->views.ui_target,
|
||||
&d3d->views.ui, video_info->views_layout.ui_dims);
|
||||
if (views_ui)
|
||||
{
|
||||
d3d9_hlsl_views_ui_begin(d3d, video_info);
|
||||
width = VIDEO_SCALE_W(video_info->dims);
|
||||
height = VIDEO_SCALE_H(video_info->dims);
|
||||
screen_vp.Width = width;
|
||||
screen_vp.Height = height;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OVERLAY
|
||||
if (d3d->overlays_enabled && overlay_behind_menu)
|
||||
if (d3d->overlays_enabled && overlay_behind_menu && !views_per_eye)
|
||||
{
|
||||
IDirect3DDevice9_SetVertexShaderConstantF(d3d->dev, 0,
|
||||
(const float*)&d3d->mvp_transposed, 4);
|
||||
@@ -7857,6 +8031,10 @@ static bool d3d9_hlsl_frame(void *data, const void *frame,
|
||||
{
|
||||
IDirect3DDevice9_SetVertexShaderConstantF(d3d->dev, 0,
|
||||
(const float*)&d3d->mvp, 4);
|
||||
#ifdef D3D9_VIEWS
|
||||
if (d3d->views.active || views_ui)
|
||||
d3d9_hlsl_views_menu_viewport(d3d, video_info->dims);
|
||||
#endif
|
||||
d3d9_hlsl_overlay_render(d3d, width, height, d3d->menu, false);
|
||||
|
||||
d3d->menu_display.offset = 0;
|
||||
@@ -7883,7 +8061,7 @@ static bool d3d9_hlsl_frame(void *data, const void *frame,
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OVERLAY
|
||||
if (d3d->overlays_enabled && !overlay_behind_menu)
|
||||
if (d3d->overlays_enabled && !overlay_behind_menu && !views_per_eye)
|
||||
{
|
||||
IDirect3DDevice9_SetVertexShaderConstantF(d3d->dev, 0,
|
||||
(const float*)&d3d->mvp_transposed, 4);
|
||||
@@ -7944,6 +8122,18 @@ static bool d3d9_hlsl_frame(void *data, const void *frame,
|
||||
IDirect3DDevice9_EndScene(d3d->dev);
|
||||
}
|
||||
|
||||
#ifdef D3D9_VIEWS
|
||||
if (views_ui)
|
||||
{
|
||||
hlsl_renderchain_t *_chain = (hlsl_renderchain_t*)d3d->renderchain_data;
|
||||
d3d9_hlsl_views_ui_end(d3d, video_info);
|
||||
IDirect3DDevice9_SetVertexShader(d3d->dev,
|
||||
(LPDIRECT3DVERTEXSHADER9)_chain->stock_shader.vprg);
|
||||
IDirect3DDevice9_SetPixelShader(d3d->dev,
|
||||
(LPDIRECT3DPIXELSHADER9)_chain->stock_shader.fprg);
|
||||
}
|
||||
#endif
|
||||
|
||||
video_driver_update_title(NULL);
|
||||
|
||||
#ifndef _XBOX
|
||||
@@ -8353,6 +8543,13 @@ static const video_poke_interface_t d3d9_hlsl_poke_interface = {
|
||||
NULL, /* hw_ring_framebuffer */
|
||||
d3d9_hlsl_update_texture,
|
||||
d3d9_hlsl_get_swap_interval_cap
|
||||
#ifdef D3D9_VIEWS
|
||||
,
|
||||
NULL, /* texture_lend */
|
||||
NULL, /* texture_lend_ready */
|
||||
NULL, /* get_last_present_wait */
|
||||
d3d9_hlsl_set_view_count
|
||||
#endif
|
||||
};
|
||||
|
||||
static void d3d9_hlsl_get_poke_interface(void *data,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/* D3D9's views pixel shaders, shader model 2, which take HLSL and Cg
|
||||
* alike. Each is main_fragment, its one constant views_c. */
|
||||
|
||||
/* A view of the frame. views_c: its first and last texel centres, so
|
||||
* filtering stops at its edge as at a texture's. */
|
||||
static const char d3d9_views_ps_rect[] =
|
||||
"uniform sampler2D s0;\n"
|
||||
"uniform float4 views_c;\n"
|
||||
"float4 main_fragment(float2 uv : TEXCOORD0) : COLOR\n"
|
||||
"{\n"
|
||||
" return float4(tex2D(s0, clamp(uv, views_c.xy, views_c.zw)).rgb,\n"
|
||||
" 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
/* The canvas, its left half the left eye: views_c.x 0 for anaglyph, by
|
||||
* Dubois' least-squares red/cyan matrices, 1 for even rows from the
|
||||
* left half and odd from the right; views_c.y its height. */
|
||||
static const char d3d9_views_ps_blend[] =
|
||||
"uniform sampler2D s0;\n"
|
||||
"uniform float4 views_c;\n"
|
||||
"float4 main_fragment(float2 uv : TEXCOORD0) : COLOR\n"
|
||||
"{\n"
|
||||
" float2 l_uv = float2(uv.x * 0.5, uv.y);\n"
|
||||
" float3 l = tex2D(s0, l_uv).rgb;\n"
|
||||
" float3 r = tex2D(s0, l_uv + float2(0.5, 0.0)).rgb;\n"
|
||||
" float odd = fmod(floor(uv.y * views_c.y), 2.0);\n"
|
||||
" float3 c = float3(\n"
|
||||
" dot(l, float3( 0.456100, 0.500484, 0.176381))\n"
|
||||
" + dot(r, float3(-0.0434706, -0.0879388, -0.00155529)),\n"
|
||||
" dot(l, float3(-0.0400822, -0.0378246, -0.0157589))\n"
|
||||
" + dot(r, float3( 0.378476, 0.73364, -0.0184503)),\n"
|
||||
" dot(l, float3(-0.0152161, -0.0205971, -0.00546856))\n"
|
||||
" + dot(r, float3(-0.0721527, -0.112961, 1.2264)));\n"
|
||||
" float3 row = lerp(l, r, odd);\n"
|
||||
" return float4(lerp(saturate(c), row, views_c.x), 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
/* The UI layer, premultiplied */
|
||||
static const char d3d9_views_ps_layer[] =
|
||||
"uniform sampler2D s0;\n"
|
||||
"float4 main_fragment(float2 uv : TEXCOORD0) : COLOR\n"
|
||||
"{\n"
|
||||
" return tex2D(s0, uv);\n"
|
||||
"}\n";
|
||||
@@ -270,7 +270,7 @@ EXACT_CASES = [
|
||||
EXACT_HW = {'gl': ('off', 'gl_compat'),
|
||||
'glcore': ('off', 'gl', 'gl_topleft'), 'vulkan': ('off', 'vulkan'),
|
||||
'gl1': ('off',), 'd3d10': ('off',), 'd3d11': ('off', 'd3d11'),
|
||||
'd3d12': ('off', 'd3d12')}
|
||||
'd3d12': ('off', 'd3d12'), 'd3d9_hlsl': ('off',)}
|
||||
# Drivers whose views take a preset: on the others a preset draws the
|
||||
# frame whole, so the cases that need one are left out there.
|
||||
PRESET_DRIVERS = ('glcore', 'vulkan', 'd3d10', 'd3d11', 'd3d12')
|
||||
@@ -316,8 +316,8 @@ SMOOTH_SETTINGS = dict(EXACT_SETTINGS, video_smooth='true')
|
||||
LINEAR_PRESET = os.path.join(HERE, 'history_linear.slangp')
|
||||
# Drivers that draw the stock chain's views straight from the frame,
|
||||
# and that draw a frame whole straight from a larger texture.
|
||||
DIRECT_DRIVERS = ('gl1', 'gl', 'glcore', 'vulkan', 'd3d10', 'd3d11',
|
||||
'd3d12')
|
||||
DIRECT_DRIVERS = ('gl1', 'gl', 'glcore', 'vulkan', 'd3d9_hlsl', 'd3d10',
|
||||
'd3d11', 'd3d12')
|
||||
DIRECT_RE = re.compile(r'\] Views drawn straight from the frame\.')
|
||||
GL_PRESET_RE = re.compile(r'\[GL\] Views drawn through the preset')
|
||||
WHOLE_DRIVERS = ('glcore',)
|
||||
|
||||
Reference in new issue
Block a user