Skip to content

Commit e4e89f4

Browse files
committed
Add the ability to change clip space Y axis orientation
1 parent bcc3efb commit e4e89f4

6 files changed

Lines changed: 32 additions & 21 deletions

File tree

Primer.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,12 @@ Field | Default | Description
199199
200200
`DkDeviceFlags_*` | Default | Description
201201
---------------------|---------|--------------------------------------------------
202-
`DepthZeroToOne` | ✓ | Clip space Z is [0, 1] like Vulkan
202+
`DepthZeroToOne` | ✓ | Clip space Z is [0, 1] like Vulkan/Direct3D/Metal
203203
`DepthMinusOneToOne` | | Clip space Z is [-1, 1] like OpenGL
204204
`OriginUpperLeft` | ✓ | Image rows are stored sequentially from top to bottom, with 0.0 corresponding to the top edge of the image and 1.0 (or the image height if non-normalized) corresponding to the bottom
205205
`OriginLowerLeft` | | Image rows are stored sequentially from bottom to top, with 0.0 corresponding to the bottom edge of the image and 1.0 (or the image height if non-normalized) corresponding to the top
206+
`YAxisPointsUp` | ✓ | Clip space Y axis points up like OpenGL/Direct3D/Metal
207+
`YAxisPointsDown` | | Clip space Y axis points down like Vulkan
206208
207209
The debugging version of the library can use an optional callback (`cbDebug`). There are two situations in which deko3d makes usage of the debug callback:
208210
@@ -218,7 +220,7 @@ In the release version of deko3d, warnings and parameter/state validation don't
218220
By default if memory allocation callbacks are not provided, deko3d uses the standard heap (i.e. malloc/free) for dynamic memory allocations.
219221
220222
`gl_FragCoord` in fragment shaders obeys the device origin mode when it comes to the Y axis and has pixel centers at half-integers, *with GLSL layout qualifiers having absolutely no effect*.
221-
Please note that regardless of the Origin setting, the clip space Y axis points *up* like in OpenGL. Clip space X and Y are both in the range [-1, 1] as well.
223+
Please also note that the origin mode and clip space Y axis orientation can be configured independently of each other. Clip space X and Y are both in the range [-1, 1] as well.
222224
223225
The current GPU tick can be queried without queuing a command buffer with `dkDeviceGetCurrentTimestamp`, and converted back and forth to nanoseconds using `dkNsToTimestamp`/`dkTimestampToNs`. See also [counters](#counters).
224226

include/deko3d.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ enum
117117
DkDeviceFlags_DepthMinusOneToOne = 1U << 8,
118118
DkDeviceFlags_OriginUpperLeft = 0U << 9,
119119
DkDeviceFlags_OriginLowerLeft = 1U << 9,
120+
DkDeviceFlags_YAxisPointsUp = 0U << 10,
121+
DkDeviceFlags_YAxisPointsDown = 1U << 10,
120122
};
121123

122124
typedef struct DkDeviceMaker

source/dk_device.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,16 @@ class Device
9090
constexpr GpuInfo const& getGpuInfo() const noexcept { return m_gpuInfo; }
9191

9292
bool isDepthModeOpenGL() const noexcept { return (m_maker.flags & DkDeviceFlags_DepthMinusOneToOne) != 0; }
93-
bool isOriginModeOpenGL() const noexcept { return (m_maker.flags & DkDeviceFlags_OriginLowerLeft) != 0; }
93+
bool isOriginLowerLeft() const noexcept { return (m_maker.flags & DkDeviceFlags_OriginLowerLeft) != 0; }
94+
bool isYAxisPointsDown() const noexcept { return (m_maker.flags & DkDeviceFlags_YAxisPointsDown) != 0; }
95+
96+
// Rasterizer uses framebuffer coordinates (with +Y = down) to calculate winding direction.
97+
// In LowerLeft mode images are stored upside down, so triangle winding must be flipped.
98+
bool windingFlip() const noexcept { return isOriginLowerLeft(); }
99+
100+
// Rasterizer expects clip space +Y to point down. In UpperLeft mode with Y pointing up
101+
// (or LowerLeft mode with Y pointing down) we need to flip the incoming Y coordinate.
102+
bool viewportFlipY() const noexcept { return !isYAxisPointsDown() ^ !isOriginLowerLeft(); }
94103

95104
DkResult initialize() noexcept;
96105
~Device();

source/dk_swapchain.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ DkResult Swapchain::initialize(void* nativeWindow, DkImage const* const images[]
9898
// If the origin mode is lower_left (i.e. OpenGL style), all rendered images are upside down
9999
// (as in the BMP format). The compositor, of course, expects images to be stored the normal
100100
// way (with Y pointing down), so we must instruct it to vertically flip whatever we feed it.
101-
if (getDevice()->isOriginModeOpenGL())
101+
if (getDevice()->isOriginLowerLeft())
102102
nwindowSetTransform(m_nwin, HAL_TRANSFORM_FLIP_V);
103103

104104
return DkResult_Success;

source/maxwell/engine_3d.def

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -419,12 +419,15 @@ engine _3D 0xB197;
419419
0x4E6 StencilFrontFuncMask;
420420
0x4E7 StencilFrontMask;
421421

422-
0x4EB SetWindowOriginMode bits (
423-
0 NegateY; // wtf
424-
4 Mode enum ( // nouveau calls this TriangleRastFlip
425-
0 UpperLeft; // as in Direct3D/Vulkan
426-
1 LowerLeft; // as in OpenGL
422+
0x4EB SetWindowOrigin bits (
423+
// Value of special register 0x12 (SR_Y_DIRECTION)
424+
0 Mode enum (
425+
0 UpperLeft; // SR_Y_DIRECTION = +1.0
426+
1 LowerLeft; // SR_Y_DIRECTION = -1.0
427427
);
428+
429+
// Framebuffer orientation for the purposes of calculating polygon winding (CW/CCW)
430+
4 FlipY bool; // 0=normal, 1=upside down
428431
);
429432
0x4EC LineWidthSmooth;
430433
0x4ED LineWidthAliased;

source/maxwell/gpu_3d_base.cpp

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,9 @@ void Queue::setup3DEngine()
147147
w << MacroSetRegisterInArray<E::ViewportTransform>(E::ViewportTransform::TranslateZ{}, isDepthOpenGL ? 0.5f : 0.0f);
148148

149149
// Configure viewport transform XY to convert [-1,1] into [0,1]: newXY = 0.5*oldXY + 0.5
150-
// Additionally, for UpperLeft origin mode, the Y value needs to be reversed since the incoming Y coordinate points up,
151-
// and that needs to be fixed to point down.
152-
// Also, SetWindowOriginMode seems to affect how the hardware treats polygons as front-facing or back-facing,
153-
// but it doesn't actually flip rendering.
154-
bool isOriginOpenGL = getDevice()->isOriginModeOpenGL();
155-
w << CmdInline(3D, SetWindowOriginMode{}, isOriginOpenGL ? E::SetWindowOriginMode::Mode::LowerLeft : E::SetWindowOriginMode::Mode::UpperLeft);
150+
w << CmdInline(3D, SetWindowOrigin{}, E::SetWindowOrigin::FlipY{getDevice()->windingFlip()});
156151
w << MacroSetRegisterInArray<E::ViewportTransform>(E::ViewportTransform::ScaleX{}, +0.5f);
157-
w << MacroSetRegisterInArray<E::ViewportTransform>(E::ViewportTransform::ScaleY{}, isOriginOpenGL ? +0.5f : -0.5f);
152+
w << MacroSetRegisterInArray<E::ViewportTransform>(E::ViewportTransform::ScaleY{}, getDevice()->viewportFlipY() ? -0.5f : +0.5f);
158153
w << MacroSetRegisterInArray<E::ViewportTransform>(E::ViewportTransform::TranslateX{}, +0.5f);
159154
w << MacroSetRegisterInArray<E::ViewportTransform>(E::ViewportTransform::TranslateY{}, +0.5f);
160155
w << MacroSetRegisterInArray<E::Viewport>(E::Viewport::Horizontal{}, 0U | (1U << 16)); // x=0 w=1
@@ -323,8 +318,8 @@ void dkCmdBufSetViewports(DkCmdBuf obj, uint32_t firstId, DkViewport const viewp
323318
DK_DEBUG_BAD_INPUT(firstId > DK_NUM_VIEWPORTS || numViewports > DK_NUM_VIEWPORTS || (firstId+numViewports) > DK_NUM_VIEWPORTS, "viewport range out of bounds");
324319
DK_DEBUG_NON_NULL_ARRAY(viewports, numViewports);
325320

326-
bool isOriginOpenGL = obj->getDevice()->isOriginModeOpenGL();
327-
bool isDepthOpenGL = obj->getDevice()->isDepthModeOpenGL();
321+
bool viewportFlipY = obj->getDevice()->viewportFlipY();
322+
bool isDepthOpenGL = obj->getDevice()->isDepthModeOpenGL();
328323

329324
CmdBufWriter w{obj};
330325
w.reserve(12*numViewports);
@@ -339,7 +334,7 @@ void dkCmdBufSetViewports(DkCmdBuf obj, uint32_t firstId, DkViewport const viewp
339334
float depthRange = v.far - v.near;
340335

341336
float scaleX = halfWidth;
342-
float scaleY = isOriginOpenGL ? halfHeight : (-halfHeight);
337+
float scaleY = viewportFlipY ? (-halfHeight) : halfHeight;
343338
float scaleZ = isDepthOpenGL ? 0.5f*depthRange : depthRange;
344339

345340
float offsetX = v.x + halfWidth;
@@ -431,7 +426,7 @@ void Queue::decompressSurface(DkImage const* image)
431426
w << ColorTargetBindCmds(rt, 0);
432427
w << CmdInline(3D, MultisampleMode{}, DkMsMode_1x);
433428
w << Cmd(3D, ScreenScissorHorizontal{}, rt.m_width<<16, rt.m_height<<16);
434-
w << CmdInline(3D, SetWindowOriginMode{}, E::SetWindowOriginMode::Mode::LowerLeft); // ??
429+
w << CmdInline(3D, SetWindowOrigin{}, E::SetWindowOrigin::FlipY{}); // ??
435430
w << CmdInline(3D, SetMultisampleRasterEnable{}, 0);
436431

437432
w << CmdInline(3D, SurfaceDecompress{}, 0);
@@ -440,7 +435,7 @@ void Queue::decompressSurface(DkImage const* image)
440435
w << ColorTargetBindCmds(rt, 0);
441436
w << CmdInline(3D, MultisampleMode{}, DkMsMode_1x);
442437
w << Cmd(3D, ScreenScissorHorizontal{}, rt.m_width<<16, rt.m_height<<16);
443-
w << CmdInline(3D, SetWindowOriginMode{}, E::SetWindowOriginMode::Mode::LowerLeft); // ??
438+
w << CmdInline(3D, SetWindowOrigin{}, E::SetWindowOrigin::FlipY{}); // ??
444439
w << CmdInline(3D, SetMultisampleRasterEnable{}, 0);
445440

446441
w << SetShadowRamControl(SRC::MethodTrackWithFilter);

0 commit comments

Comments
 (0)