If we have clip control, viewport_miscpart_cc applies both the half pixel offset and the filling convention nudge. We always have integer pixel centers in this <= d3d9-only codepath.
Signed-off-by: Stefan Dösinger stefan@codeweavers.com
---
Taking d3d10+ pixel centers out of consideration here keeps the next patch simpler because we don't have to account for the situation where we have to apply the nudge only because we don't have clip control but also somehow have GL-style pixel centers. It seems like a non-obvious set of conditions for catching a case we know won't is dead code. Do add an ERR though because it actually hardwires client lib knowledge into wined3d. --- dlls/wined3d/utils.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c index 4019dd4d812..0a1e0707359 100644 --- a/dlls/wined3d/utils.c +++ b/dlls/wined3d/utils.c @@ -5550,12 +5550,16 @@ void get_projection_matrix(const struct wined3d_context *context, const struct w * driver, but small enough to prevent it from interfering with any * anti-aliasing. */
+ /* Projection matrices are <= d3d9, which all have integer pixel centers. */ + if (!(d3d_info->wined3d_creation_flags & WINED3D_PIXEL_CENTER_INTEGER)) + ERR("Did not expect to enter this codepath without WINED3D_PIXEL_CENTER_INTEGER.\n"); + clip_control = d3d_info->clip_control; flip = !clip_control && context->render_offscreen; - if (!clip_control && d3d_info->wined3d_creation_flags & WINED3D_PIXEL_CENTER_INTEGER) + if (!clip_control) center_offset = 63.0f / 64.0f; else - center_offset = -1.0f / 64.0f; + center_offset = 0.0f;
if (context->last_was_rhw) {
This fixes stray lines in GameFace GUIs, e.g. in World of Tanks.
Signed-off-by: Stefan Dösinger stefan@codeweavers.com
---
Version 2: *) Use a binary search for the fixup value. *) Change how an unexpected test result is detected. *) Add a comment about this to the Vulkan backend. *) Small wording changes.
Re Vulkan: If you can refer me to the place in the spec that mandates a top-left filling convention I am more than happy to add it in the comment. I really don't want to add this detection mess to the Vulkan backend.
I am also more than happy to just drop this nudge thingy unconditionally. I am not aware of a card + game combination that is fixed by this. Spore doesn't need it on my gf9600. I didn't bother to download Everquest, I expect it to have changed a lot in the past 10 years. I don't have access to a dx9 level card right now, so I can't entirely rule out that Spore needs this nudge on those cards.
The ddraw tests started succeeding in the todo block for a few of the tested pixels. I looked at the output results and I don't think it happened because the Z values are more precise now. I think a few bits flipped (in the random readback we get) and now a few of those tests were inside the expected diff. --- dlls/d3d11/tests/d3d11.c | 1 - dlls/ddraw/tests/ddraw4.c | 6 +- dlls/ddraw/tests/ddraw7.c | 6 +- dlls/wined3d/adapter_gl.c | 50 ++++++++++++++ dlls/wined3d/adapter_vk.c | 10 +++ dlls/wined3d/state.c | 3 +- dlls/wined3d/utils.c | 118 ++++++++++++++++++++++++++++++--- dlls/wined3d/wined3d_private.h | 13 +++- 8 files changed, 189 insertions(+), 18 deletions(-)
diff --git a/dlls/d3d11/tests/d3d11.c b/dlls/d3d11/tests/d3d11.c index d8c10ab054c..9bff90bc1e7 100644 --- a/dlls/d3d11/tests/d3d11.c +++ b/dlls/d3d11/tests/d3d11.c @@ -28155,7 +28155,6 @@ static void test_fractional_viewports(void) ok(compare_float(v->x, expected.x, 0) && compare_float(v->y, expected.y, 0), "Got fragcoord {%.8e, %.8e}, expected {%.8e, %.8e} at (%u, %u), offset %.8e.\n", v->x, v->y, expected.x, expected.y, x, y, viewport_offsets[i]); - todo_wine ok(compare_float(v->z, expected.z, 2) && compare_float(v->w, expected.w, 2), "Got texcoord {%.8e, %.8e}, expected {%.8e, %.8e} at (%u, %u), offset %.8e.\n", v->z, v->w, expected.z, expected.w, x, y, viewport_offsets[i]); diff --git a/dlls/ddraw/tests/ddraw4.c b/dlls/ddraw/tests/ddraw4.c index 6b514b15e25..4f052256882 100644 --- a/dlls/ddraw/tests/ddraw4.c +++ b/dlls/ddraw/tests/ddraw4.c @@ -16118,8 +16118,10 @@ static void test_depth_readback(void) /* The ddraw4 version of this test behaves similarly to the ddraw7 version on Nvidia GPUs, * except that Geforce 7 also returns garbage data in D24S8, whereas the ddraw7 version * returns 0 for that format. Give up on pre-filtering formats, accept Nvidia as generally - * broken here, but still expect at least one format (D16 or D24X8 in practise) to pass. */ - todo_wine_if(tests[i].todo) + * broken here, but still expect at least one format (D16 or D24X8 in practise) to pass. + * + * Some of the tested places pass on some GPUs on Wine by accident. */ + todo_wine_if(tests[i].todo && !compare_uint(expected_depth, depth, max_diff)) ok(compare_uint(expected_depth, depth, max_diff) || ddraw_is_nvidia(ddraw), "Test %u: Got depth 0x%08x (diff %d), expected 0x%08x+/-%u, at %u, %u.\n", i, depth, expected_depth - depth, expected_depth, max_diff, x, y); diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c index 4c42d6f4b64..4402f2d93b5 100644 --- a/dlls/ddraw/tests/ddraw7.c +++ b/dlls/ddraw/tests/ddraw7.c @@ -15597,8 +15597,10 @@ static void test_depth_readback(void) * Geforce GTX 650 has working D16 and D24, but D24S8 returns 0. * * Arx Fatalis is broken on the Geforce 9 in the same way it was broken in Wine (bug 43654). - * The !tests[i].s_depth is supposed to rule out D16 on GF9 and D24X8 on GF7. */ - todo_wine_if(tests[i].todo) + * The !tests[i].s_depth is supposed to rule out D16 on GF9 and D24X8 on GF7. + * + * Some of the tested places pass on some GPUs on Wine by accident. */ + todo_wine_if(tests[i].todo && !compare_uint(expected_depth, depth, max_diff)) ok(compare_uint(expected_depth, depth, max_diff) || (ddraw_is_nvidia(ddraw) && (all_zero || all_one || !tests[i].s_depth)), "Test %u: Got depth 0x%08x (diff %d), expected 0x%08x+/-%u, at %u, %u.\n", diff --git a/dlls/wined3d/adapter_gl.c b/dlls/wined3d/adapter_gl.c index bba728e2fb5..386d26828dc 100644 --- a/dlls/wined3d/adapter_gl.c +++ b/dlls/wined3d/adapter_gl.c @@ -5134,6 +5134,7 @@ static void wined3d_adapter_gl_init_d3d_info(struct wined3d_adapter_gl *adapter_ d3d_info->scaled_resolve = !!gl_info->supported[EXT_FRAMEBUFFER_MULTISAMPLE_BLIT_SCALED]; d3d_info->pbo = !!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]; d3d_info->feature_level = feature_level_from_caps(gl_info, &shader_caps, &fragment_caps); + d3d_info->filling_convention_nudge = gl_info->filling_convention_nudge;
if (gl_info->supported[ARB_TEXTURE_MULTISAMPLE]) d3d_info->multisample_draw_location = WINED3D_LOCATION_TEXTURE_RGB; @@ -5141,6 +5142,53 @@ static void wined3d_adapter_gl_init_d3d_info(struct wined3d_adapter_gl *adapter_ d3d_info->multisample_draw_location = WINED3D_LOCATION_RB_MULTISAMPLE; }
+static float wined3d_adapter_find_fill_nudge(struct wined3d_caps_gl_ctx *ctx) +{ + static const float test_array[] = + { + 0.0f, + -1.0f / 1024.0f, + -1.0f / 512.0f, + -1.0f / 256.0f, + -1.0f / 128.0f, + -1.0f / 64.0f + }; + unsigned int good = ARRAY_SIZE(test_array), bad = 0, test; + float value; + + if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) + goto end; + + while (good != bad) + { + test = (good + bad) / 2; + value = test_array[test]; + TRACE("Good %u bad %u, test %u.\n", good, bad, test); + if (wined3d_caps_gl_ctx_test_filling_convention(ctx, value)) + good = test; + else + bad = test + 1; + } + + if (good < ARRAY_SIZE(test_array)) + { + value = test_array[good]; + if (value) + WARN("Using a filling convention fixup nudge of -1/%f.\n", -1.0f / value); + else + TRACE("No need for a filling convetion nudge.\n"); + + return value; + } + + FIXME("Did not find a way to get the filling convention we want.\n"); + +end: + /* This value was used unconditionally before the dynamic test function was + * introduced. */ + return -1.0f / 64.0f; +} + static BOOL wined3d_adapter_gl_init(struct wined3d_adapter_gl *adapter_gl, unsigned int ordinal, unsigned int wined3d_creation_flags) { @@ -5226,6 +5274,8 @@ static BOOL wined3d_adapter_gl_init(struct wined3d_adapter_gl *adapter_gl, return FALSE; }
+ gl_info->filling_convention_nudge = wined3d_adapter_find_fill_nudge(&caps_gl_ctx); + wined3d_adapter_gl_init_d3d_info(adapter_gl, wined3d_creation_flags);
if (!adapter_gl->a.d3d_info.shader_color_key) diff --git a/dlls/wined3d/adapter_vk.c b/dlls/wined3d/adapter_vk.c index 18c73312daf..324f4316901 100644 --- a/dlls/wined3d/adapter_vk.c +++ b/dlls/wined3d/adapter_vk.c @@ -2206,6 +2206,16 @@ static void wined3d_adapter_vk_init_d3d_info(struct wined3d_adapter_vk *adapter_ d3d_info->pbo = true; d3d_info->feature_level = feature_level_from_caps(&shader_caps);
+ /* Like GL, Vulkan doesn't explicitly specify a filling convention and only mandates that a + * shared edge of two adjacent triangles generate a fragment for exactly one of the triangles. + * vktRasterizationTests.cpp from the vulkan CTS tests this by drawing two triangles with a + * shared edge with additive blending. + * + * However, every Vulkan implementation we have seen so far uses a top-left rule. Hardware + * that differs either predates Vulkan (d3d9 class HW, GeForce 9xxx) or behaves the way we + * want in Vulkan (MacOS Radeon driver through MoltenVK). */ + d3d_info->filling_convention_nudge = 0.0; + d3d_info->multisample_draw_location = WINED3D_LOCATION_TEXTURE_RGB; }
diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c index 8316269afcf..5c1c69fb650 100644 --- a/dlls/wined3d/state.c +++ b/dlls/wined3d/state.c @@ -4233,13 +4233,14 @@ static void viewport_miscpart_cc(struct wined3d_context *context, const struct wined3d_gl_info *gl_info = wined3d_context_gl(context)->gl_info; /* See get_projection_matrix() in utils.c for a discussion about those values. */ float pixel_center_offset = context->d3d_info->wined3d_creation_flags - & WINED3D_PIXEL_CENTER_INTEGER ? 63.0f / 128.0f : -1.0f / 128.0f; + & WINED3D_PIXEL_CENTER_INTEGER ? 0.5f : 0.0f; struct wined3d_viewport vp[WINED3D_MAX_VIEWPORTS]; GLdouble depth_ranges[2 * WINED3D_MAX_VIEWPORTS]; GLfloat viewports[4 * WINED3D_MAX_VIEWPORTS]; unsigned int i, reset_count = 0; float min_z, max_z;
+ pixel_center_offset += context->d3d_info->filling_convention_nudge / 2.0f; get_viewports(context, state, state->viewport_count, vp);
GL_EXTCALL(glClipControl(context->render_offscreen ? GL_UPPER_LEFT : GL_LOWER_LEFT, GL_ZERO_TO_ONE)); diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c index 0a1e0707359..96351d7ead6 100644 --- a/dlls/wined3d/utils.c +++ b/dlls/wined3d/utils.c @@ -3928,6 +3928,100 @@ BOOL wined3d_caps_gl_ctx_test_viewport_subpixel_bits(struct wined3d_caps_gl_ctx return TRUE; }
+bool wined3d_caps_gl_ctx_test_filling_convention(struct wined3d_caps_gl_ctx *ctx, float nudge) +{ + static const struct wined3d_color red = {1.0f, 0.0f, 0.0f, 1.0f}; + const struct wined3d_gl_info *gl_info = ctx->gl_info; + unsigned int x, y, clear = 0, draw = 0; + GLuint texture, fbo; + DWORD readback[8][8]; + + /* This is a very simple test to find out how GL handles polygon edges: + * Draw a quad exactly through 4 pixel centers in an 8x8 viewport and see + * which pixel it ends up in. So far we've seen top left and bottom + * left conventions. This test may produce unexpected results if the + * driver forces multisampling on us. + * + * If we find a bottom-left filling behavior we also nudge the x-axis + * by the same amount. This is necessary to keep diagonals that go + * through the pixel center intact. + * + * Note that we are ignoring some settings that might influence the + * driver: How we switch GL to an upper-left coordinate system, + * shaders vs fixed function GL. Testing these isn't possible with + * the current draw_test_quad() infrastructure. Also the test is + * skipped if we are not using FBOs. Drawing into the onscreen + * frame buffer may also yield different driver behavior. + * + * The minimum nudge also depends on the viewport size, although + * the relation between those two is GPU dependent and not exactly + * sensible. E.g. a 8192x8192 viewport on a GeForce 9 needs at + * least a nudge of 1/240.9, whereas a 8x8 one needs 1/255.982; + * 32x32 needs 1/255.935. 4x4 and lower are happy with something + * below 1/256. The 8x8 size below has been arbitrarily chosen to + * get a useful result out of that card and avoid allocating a + * gigantic texture during library init. + * + * Newer cards usually do the right thing anyway. In cases where + * they do not (e.g. Radeon GPUs in a macbookpro14,3 running MacOS) + * a nudge of 1/2^20 is enough. */ + const struct wined3d_vec3 edge_geometry[] = + { + {(-1.0f + nudge) / 8.0f, (-1.0f + nudge) / 8.0f, 0.0f}, + {( 1.0f + nudge) / 8.0f, (-1.0f + nudge) / 8.0f, 0.0f}, + {(-1.0f + nudge) / 8.0f, ( 1.0f + nudge) / 8.0f, 0.0f}, + {( 1.0f + nudge) / 8.0f, ( 1.0f + nudge) / 8.0f, 0.0f}, + }; + + gl_info->gl_ops.gl.p_glGenTextures(1, &texture); + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, texture); + gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 8, 8, 0, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); + gl_info->fbo_ops.glGenFramebuffers(1, &fbo); + gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, fbo); + gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, texture, 0); + checkGLcall("create resources"); + + gl_info->gl_ops.gl.p_glViewport(0, 0, 8, 8); + gl_info->gl_ops.gl.p_glClearColor(0.0f, 0.0f, 1.0f, 1.0f); + gl_info->gl_ops.gl.p_glClear(GL_COLOR_BUFFER_BIT); + + draw_test_quad(ctx, edge_geometry, &red); + checkGLcall("draw"); + + gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, texture); + gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_2D, 0, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, readback); + checkGLcall("readback"); + + gl_info->gl_ops.gl.p_glDeleteTextures(1, &texture); + gl_info->fbo_ops.glDeleteFramebuffers(1, &fbo); + gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, 0); + checkGLcall("delete resources"); + + /* We expect that exactly one fragment is generated. */ + for (y = 0; y < ARRAY_SIZE(readback); ++y) + { + for (x = 0; x < ARRAY_SIZE(readback[0]); ++x) + { + if (readback[y][x] == 0xff0000ff) + clear++; + else if (readback[y][x] == 0xffff0000) + draw++; + } + } + + if (clear != 63 || draw != 1) + { + FIXME("Unexpected filling convention test result.\n"); + return FALSE; + } + + /* One pixel was drawn, check if it is the expect one */ + return readback[3][3] == 0xffff0000; +} static float wined3d_adapter_find_polyoffset_scale(struct wined3d_caps_gl_ctx *ctx, GLenum format) { const struct wined3d_gl_info *gl_info = ctx->gl_info; @@ -5540,15 +5634,19 @@ void get_projection_matrix(const struct wined3d_context *context, const struct w * - We need to flip along the y-axis in case of offscreen rendering. * - OpenGL Z range is {-Wc,...,Wc} while D3D Z range is {0,...,Wc}. * - <= D3D9 coordinates refer to pixel centers while GL coordinates - * refer to pixel corners. - * - D3D has a top-left filling convention. We need to maintain this - * even after the y-flip mentioned above. - * In order to handle the last two points, we translate by - * (63.0 / 128.0) / VPw and (63.0 / 128.0) / VPh. This is equivalent to - * translating slightly less than half a pixel. We want the difference to - * be large enough that it doesn't get lost due to rounding inside the - * driver, but small enough to prevent it from interfering with any - * anti-aliasing. */ + * refer to pixel corners. D3D10 fixed this particular oddity. + * - D3D has a top-left filling convention while GL does not specify + * a particular behavior, other than that that the GL implementation + * needs to be consistent. + * + * In order to handle the pixel center, we translate by 0.5 / VPw and + * 0.5 / VPh. We test the filling convention during adapter init and + * add a small offset to correct it if necessary. See + * wined3d_caps_gl_ctx_test_filling_convention() for more details on how + * we test GL and considerations regarding the added nudge value. + * + * If we have GL_ARB_clip_control we take care of all this through + * viewport properties and don't have to translate geometry. */
/* Projection matrices are <= d3d9, which all have integer pixel centers. */ if (!(d3d_info->wined3d_creation_flags & WINED3D_PIXEL_CENTER_INTEGER)) @@ -5557,7 +5655,7 @@ void get_projection_matrix(const struct wined3d_context *context, const struct w clip_control = d3d_info->clip_control; flip = !clip_control && context->render_offscreen; if (!clip_control) - center_offset = 63.0f / 64.0f; + center_offset = 1.0f + d3d_info->filling_convention_nudge; else center_offset = 0.0f;
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 8c49e694fa9..0990bffed21 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -243,6 +243,8 @@ struct wined3d_d3d_info enum wined3d_feature_level feature_level;
DWORD multisample_draw_location; + + float filling_convention_nudge; };
static const struct color_fixup_desc COLOR_FIXUP_IDENTITY = @@ -3252,6 +3254,7 @@ struct wined3d_gl_info DWORD quirks; BOOL supported[WINED3D_GL_EXT_COUNT]; GLint wrap_lookup[WINED3D_TADDRESS_MIRROR_ONCE - WINED3D_TADDRESS_WRAP + 1]; + float filling_convention_nudge;
HGLRC (WINAPI *p_wglCreateContextAttribsARB)(HDC dc, HGLRC share, const GLint *attribs); struct opengl_funcs gl_ops; @@ -3500,6 +3503,7 @@ BOOL wined3d_adapter_vk_init_format_info(struct wined3d_adapter_vk *adapter_vk, UINT64 adapter_adjust_memory(struct wined3d_adapter *adapter, INT64 amount) DECLSPEC_HIDDEN;
BOOL wined3d_caps_gl_ctx_test_viewport_subpixel_bits(struct wined3d_caps_gl_ctx *ctx) DECLSPEC_HIDDEN; +bool wined3d_caps_gl_ctx_test_filling_convention(struct wined3d_caps_gl_ctx *ctx, float nudge) DECLSPEC_HIDDEN;
void install_gl_compat_wrapper(struct wined3d_gl_info *gl_info, enum wined3d_gl_extension ext) DECLSPEC_HIDDEN;
@@ -5680,10 +5684,15 @@ static inline void shader_get_position_fixup(const struct wined3d_context *conte float center_offset; unsigned int i;
+ /* See get_projection_matrix() in utils.c for a discussion of the position fixup. + * This function here also applies to d3d10+ which does not need adjustment for + * integer pixel centers, but it may need the filling convention nudge. */ if (context->d3d_info->wined3d_creation_flags & WINED3D_PIXEL_CENTER_INTEGER) - center_offset = 63.0f / 64.0f; + center_offset = 1.0f; else - center_offset = -1.0f / 64.0f; + center_offset = 0.0f; + + center_offset += context->d3d_info->filling_convention_nudge;
for (i = 0; i < fixup_count; ++i) {
On Wed, 29 Sept 2021 at 21:43, Stefan Dösinger stefan@codeweavers.com wrote:
diff --git a/dlls/wined3d/adapter_gl.c b/dlls/wined3d/adapter_gl.c index bba728e2fb5..386d26828dc 100644 --- a/dlls/wined3d/adapter_gl.c +++ b/dlls/wined3d/adapter_gl.c @@ -5134,6 +5134,7 @@ static void wined3d_adapter_gl_init_d3d_info(struct wined3d_adapter_gl *adapter_ d3d_info->scaled_resolve = !!gl_info->supported[EXT_FRAMEBUFFER_MULTISAMPLE_BLIT_SCALED]; d3d_info->pbo = !!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]; d3d_info->feature_level = feature_level_from_caps(gl_info, &shader_caps, &fragment_caps);
- d3d_info->filling_convention_nudge = gl_info->filling_convention_nudge;
"filling_convention_offset", perhaps.
+static float wined3d_adapter_find_fill_nudge(struct wined3d_caps_gl_ctx *ctx) +{
- static const float test_array[] =
- {
0.0f,
-1.0f / 1024.0f,
-1.0f / 512.0f,
-1.0f / 256.0f,
-1.0f / 128.0f,
-1.0f / 64.0f
- };
- unsigned int good = ARRAY_SIZE(test_array), bad = 0, test;
- float value;
- if (wined3d_settings.offscreen_rendering_mode != ORM_FBO)
goto end;
- while (good != bad)
- {
test = (good + bad) / 2;
value = test_array[test];
TRACE("Good %u bad %u, test %u.\n", good, bad, test);
if (wined3d_caps_gl_ctx_test_filling_convention(ctx, value))
good = test;
else
bad = test + 1;
- }
I generally prefer something along the lines of "upper"/"lower" for these. Not in the least because in some cases "bad" is the value we actually end up using.
- if (good < ARRAY_SIZE(test_array))
- {
value = test_array[good];
if (value)
WARN("Using a filling convention fixup nudge of -1/%f.\n", -1.0f / value);
else
TRACE("No need for a filling convetion nudge.\n");
"convention"
- /* Like GL, Vulkan doesn't explicitly specify a filling convention and only mandates that a
* shared edge of two adjacent triangles generate a fragment for exactly one of the triangles.
* vktRasterizationTests.cpp from the vulkan CTS tests this by drawing two triangles with a
* shared edge with additive blending.
*
I don't think the bit about the CTS is especially pertinent.
* However, every Vulkan implementation we have seen so far uses a top-left rule. Hardware
* that differs either predates Vulkan (d3d9 class HW, GeForce 9xxx) or behaves the way we
* want in Vulkan (MacOS Radeon driver through MoltenVK). */
- d3d_info->filling_convention_nudge = 0.0;
"0.0f"
- /* This is a very simple test to find out how GL handles polygon edges:
* Draw a quad exactly through 4 pixel centers in an 8x8 viewport and see
* which pixel it ends up in. So far we've seen top left and bottom
The first time I read this, my thought was "shouldn't that be 'pixels'?". The trick of course is that we're drawing a 1x1 quad through 4 adjacent pixel centres.
- /* One pixel was drawn, check if it is the expect one */
- return readback[3][3] == 0xffff0000;
"expected"
Signed-off-by: Stefan Dösinger stefan@codeweavers.com
---
There is a certain overlap between this test and test_[fractional_]viewport. This test explicitly tests which fragment lights up and how diagonals that are hit by two triangles are handled, whereas test_viewport checks for a mismatch between fragment location and position of that fragment inside the triangle. Both tests are worth having IMO.
I'll port it to ddraw-d3d11 once the merits of the test and geometry nudge have been agreed upon. --- dlls/d3d9/tests/visual.c | 487 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 487 insertions(+)
diff --git a/dlls/d3d9/tests/visual.c b/dlls/d3d9/tests/visual.c index 3820252fdef..e0ce6ade4be 100644 --- a/dlls/d3d9/tests/visual.c +++ b/dlls/d3d9/tests/visual.c @@ -27019,6 +27019,492 @@ static void test_dynamic_map_synchronization(void) DestroyWindow(window); }
+static void test_filling_convention(void) +{ + static const DWORD colour_bottom = 0x00ffff00; + static const DWORD colour_clear = 0x000000ff; + static const DWORD colour_right = 0x00000000; + static const DWORD colour_left = 0x00ff0000; + static const DWORD colour_top = 0x0000ff00; + IDirect3DSurface9 *rt, *backbuffer, *cur; + IDirect3DVertexShader9 *shader = NULL; + struct surface_readback rb; + IDirect3DDevice9 *device; + unsigned int i, j, x, y; + DWORD colour, expected; + IDirect3D9 *d3d; + ULONG refcount; + D3DCAPS9 caps; + HWND window; + HRESULT hr; + BOOL todo; + + static const unsigned int vp_size = 8; + const D3DVIEWPORT9 vp = { 0, 0, vp_size, vp_size, 0.0, 1.0 }; + static const DWORD vs_code[] = + { + 0xfffe0101, /* vs_1_1 */ + 0x0000001f, 0x80000000, 0x900f0000, /* dcl_position v0 */ + 0x0000001f, 0x8000000a, 0x900f0001, /* dcl_color0 v1 */ + 0x00000001, 0xc00f0000, 0x90e40000, /* mov oPos, v0 */ + 0x00000001, 0xd00f0000, 0x90e40001, /* mov oD0, v1 */ + 0x0000ffff /* end */ + }; + + /* This test data follows the examples in MSDN's + * "Rasterization Rules (Direct3D 9)" article at + * https://docs.microsoft.com/en-us/windows/win32/direct3d9/rasterization-rules */ + static const float eps = 1.0f / 512.0f; + const struct + { + struct vec3 position; + DWORD diffuse; + } + center_tris[] = + { + /* left */ + {{-2.5f / 4.0f, -1.5f / 4.0f, 0.0f}, colour_left}, + {{-2.5f / 4.0f, 2.5f / 4.0f, 0.0f}, colour_left}, + {{-1.5f / 4.0f, 0.5f / 4.0f, 0.0f}, colour_left}, + + /* top */ + {{-1.5f / 4.0f, 0.5f / 4.0f, 0.0f}, colour_top}, + {{-2.5f / 4.0f, 2.5f / 4.0f, 0.0f}, colour_top}, + {{-0.5f / 4.0f, 2.5f / 4.0f, 0.0f}, colour_top}, + + /* right */ + {{-0.5f / 4.0f, -1.5f / 4.0f, 0.0f}, colour_right}, + {{-1.5f / 4.0f, 0.5f / 4.0f, 0.0f}, colour_right}, + {{-0.5f / 4.0f, 2.5f / 4.0f, 0.0f}, colour_right}, + + /* bottom */ + {{-2.5f / 4.0f, -1.5f / 4.0f, 0.0f}, colour_bottom}, + {{-1.5f / 4.0f, 0.5f / 4.0f, 0.0f}, colour_bottom}, + {{-0.5f / 4.0f, -1.5f / 4.0f, 0.0f}, colour_bottom}, + + }, + edge_tris[] = + { + /* left */ + {{-2.0f / 4.0f, -1.0f / 4.0f, 0.0f}, colour_left}, + {{-2.0f / 4.0f, 3.0f / 4.0f, 0.0f}, colour_left}, + {{-1.0f / 4.0f, 1.0f / 4.0f, 0.0f}, colour_left}, + + /* top */ + {{-1.0f / 4.0f, 1.0f / 4.0f, 0.0f}, colour_top}, + {{-2.0f / 4.0f, 3.0f / 4.0f, 0.0f}, colour_top}, + {{ 0.0f / 4.0f, 3.0f / 4.0f, 0.0f}, colour_top}, + + /* right */ + {{ 0.0f / 4.0f, -1.0f / 4.0f, 0.0f}, colour_right}, + {{-1.0f / 4.0f, 1.0f / 4.0f, 0.0f}, colour_right}, + {{ 0.0f / 4.0f, 3.0f / 4.0f, 0.0f}, colour_right}, + + /* bottom */ + {{-2.0f / 4.0f, -1.0f / 4.0f, 0.0f}, colour_bottom}, + {{-1.0f / 4.0f, 1.0f / 4.0f, 0.0f}, colour_bottom}, + {{ 0.0f / 4.0f, -1.0f / 4.0f, 0.0f}, colour_bottom}, + }, + nudge_right_tris[] = + { + /* left */ + {{eps - 2.0f / 4.0f, -1.0f / 4.0f, 0.0f}, colour_left}, + {{eps - 2.0f / 4.0f, 3.0f / 4.0f, 0.0f}, colour_left}, + {{eps - 1.0f / 4.0f, 1.0f / 4.0f, 0.0f}, colour_left}, + + /* top */ + {{eps - 1.0f / 4.0f, 1.0f / 4.0f, 0.0f}, colour_top}, + {{eps - 2.0f / 4.0f, 3.0f / 4.0f, 0.0f}, colour_top}, + {{eps - 0.0f / 4.0f, 3.0f / 4.0f, 0.0f}, colour_top}, + + /* right */ + {{eps - 0.0f / 4.0f, -1.0f / 4.0f, 0.0f}, colour_right}, + {{eps - 1.0f / 4.0f, 1.0f / 4.0f, 0.0f}, colour_right}, + {{eps - 0.0f / 4.0f, 3.0f / 4.0f, 0.0f}, colour_right}, + + /* bottom */ + {{eps - 2.0f / 4.0f, -1.0f / 4.0f, 0.0f}, colour_bottom}, + {{eps - 1.0f / 4.0f, 1.0f / 4.0f, 0.0f}, colour_bottom}, + {{eps - 0.0f / 4.0f, -1.0f / 4.0f, 0.0f}, colour_bottom}, + }, + nudge_left_tris[] = + { + {{-eps - 2.0f / 4.0f, -1.0f / 4.0f, 0.0f}, colour_left}, + {{-eps - 2.0f / 4.0f, 3.0f / 4.0f, 0.0f}, colour_left}, + {{-eps - 1.0f / 4.0f, 1.0f / 4.0f, 0.0f}, colour_left}, + + /* top */ + {{-eps - 1.0f / 4.0f, 1.0f / 4.0f, 0.0f}, colour_top}, + {{-eps - 2.0f / 4.0f, 3.0f / 4.0f, 0.0f}, colour_top}, + {{-eps - 0.0f / 4.0f, 3.0f / 4.0f, 0.0f}, colour_top}, + + /* right */ + {{-eps - 0.0f / 4.0f, -1.0f / 4.0f, 0.0f}, colour_right}, + {{-eps - 1.0f / 4.0f, 1.0f / 4.0f, 0.0f}, colour_right}, + {{-eps - 0.0f / 4.0f, 3.0f / 4.0f, 0.0f}, colour_right}, + + /* bottom */ + {{-eps - 2.0f / 4.0f, -1.0f / 4.0f, 0.0f}, colour_bottom}, + {{-eps - 1.0f / 4.0f, 1.0f / 4.0f, 0.0f}, colour_bottom}, + {{-eps - 0.0f / 4.0f, -1.0f / 4.0f, 0.0f}, colour_bottom}, + }, + nudge_top_tris[] = + { + /* left */ + {{-2.0f / 4.0f, eps - 1.0f / 4.0f, 0.0f}, colour_left}, + {{-2.0f / 4.0f, eps + 3.0f / 4.0f, 0.0f}, colour_left}, + {{-1.0f / 4.0f, eps + 1.0f / 4.0f, 0.0f}, colour_left}, + + /* top */ + {{-1.0f / 4.0f, eps + 1.0f / 4.0f, 0.0f}, colour_top}, + {{-2.0f / 4.0f, eps + 3.0f / 4.0f, 0.0f}, colour_top}, + {{ 0.0f / 4.0f, eps + 3.0f / 4.0f, 0.0f}, colour_top}, + + /* right */ + {{ 0.0f / 4.0f, eps - 1.0f / 4.0f, 0.0f}, colour_right}, + {{-1.0f / 4.0f, eps + 1.0f / 4.0f, 0.0f}, colour_right}, + {{ 0.0f / 4.0f, eps + 3.0f / 4.0f, 0.0f}, colour_right}, + + /* bottom */ + {{-2.0f / 4.0f, eps - 1.0f / 4.0f, 0.0f}, colour_bottom}, + {{-1.0f / 4.0f, eps + 1.0f / 4.0f, 0.0f}, colour_bottom}, + {{ 0.0f / 4.0f, eps - 1.0f / 4.0f, 0.0f}, colour_bottom}, + }, + nudge_bottom_tris[] = + { + /* left */ + {{-2.0f / 4.0f, -eps - 1.0f / 4.0f, 0.0f}, colour_left}, + {{-2.0f / 4.0f, -eps + 3.0f / 4.0f, 0.0f}, colour_left}, + {{-1.0f / 4.0f, -eps + 1.0f / 4.0f, 0.0f}, colour_left}, + + /* top */ + {{-1.0f / 4.0f, -eps + 1.0f / 4.0f, 0.0f}, colour_top}, + {{-2.0f / 4.0f, -eps + 3.0f / 4.0f, 0.0f}, colour_top}, + {{ 0.0f / 4.0f, -eps + 3.0f / 4.0f, 0.0f}, colour_top}, + + /* right */ + {{ 0.0f / 4.0f, -eps - 1.0f / 4.0f, 0.0f}, colour_right}, + {{-1.0f / 4.0f, -eps + 1.0f / 4.0f, 0.0f}, colour_right}, + {{ 0.0f / 4.0f, -eps + 3.0f / 4.0f, 0.0f}, colour_right}, + + /* bottom */ + {{-2.0f / 4.0f, -eps - 1.0f / 4.0f, 0.0f}, colour_bottom}, + {{-1.0f / 4.0f, -eps + 1.0f / 4.0f, 0.0f}, colour_bottom}, + {{ 0.0f / 4.0f, -eps - 1.0f / 4.0f, 0.0f}, colour_bottom}, + }; + + /* FIXME: Is the POSITIONT coord system flipped vs the regular one? */ + const struct + { + struct vec4 position; + DWORD diffuse; + } + center_tris_t[] = + { + /* left */ + {{ 1.5f, 1.5f, 0.0f, 1.0f}, colour_left}, + {{ 2.5f, 3.5f, 0.0f, 1.0f}, colour_left}, + {{ 1.5f, 5.5f, 0.0f, 1.0f}, colour_left}, + + /* top */ + {{ 1.5f, 1.5f, 0.0f, 1.0f}, colour_top}, + {{ 3.5f, 1.5f, 0.0f, 1.0f}, colour_top}, + {{ 2.5f, 3.5f, 0.0f, 1.0f}, colour_top}, + + /* right */ + {{ 3.5f, 1.5f, 0.0f, 1.0f}, colour_right}, + {{ 3.5f, 5.5f, 0.0f, 1.0f}, colour_right}, + {{ 2.5f, 3.5f, 0.0f, 1.0f}, colour_right}, + + /* bottom */ + {{ 2.5f, 3.5f, 0.0f, 1.0f}, colour_bottom}, + {{ 3.5f, 5.5f, 0.0f, 1.0f}, colour_bottom}, + {{ 1.5f, 5.5f, 0.0f, 1.0f}, colour_bottom}, + }, + edge_tris_t[] = + { + /* left */ + {{ 2.0f, 1.0f, 0.0f, 1.0f}, colour_left}, + {{ 3.0f, 3.0f, 0.0f, 1.0f}, colour_left}, + {{ 2.0f, 5.0f, 0.0f, 1.0f}, colour_left}, + + /* top */ + {{ 2.0f, 1.0f, 0.0f, 1.0f}, colour_top}, + {{ 4.0f, 1.0f, 0.0f, 1.0f}, colour_top}, + {{ 3.0f, 3.0f, 0.0f, 1.0f}, colour_top}, + + /* right */ + {{ 4.0f, 1.0f, 0.0f, 1.0f}, colour_right}, + {{ 4.0f, 5.0f, 0.0f, 1.0f}, colour_right}, + {{ 3.0f, 3.0f, 0.0f, 1.0f}, colour_right}, + + /* bottom */ + {{ 3.0f, 3.0f, 0.0f, 1.0f}, colour_bottom}, + {{ 4.0f, 5.0f, 0.0f, 1.0f}, colour_bottom}, + {{ 2.0f, 5.0f, 0.0f, 1.0f}, colour_bottom}, + }; + + const struct + { + const void *geometry; + size_t stride; + DWORD fvf; + const char *expected[8]; + } + tests[] = + { + { + center_tris, + sizeof(center_tris[0]), + D3DFVF_XYZ | D3DFVF_DIFFUSE, + { + " ", + " ", + " TT ", + " LR ", + " LR ", + " BB ", + " ", + " " + } + }, + { + edge_tris, + sizeof(edge_tris[0]), + D3DFVF_XYZ | D3DFVF_DIFFUSE, + { + " ", + " TT ", + " LT ", + " LR ", + " LB ", + " ", + " ", + " " + } + }, + { + nudge_right_tris, + sizeof(nudge_right_tris[0]), + D3DFVF_XYZ | D3DFVF_DIFFUSE, + { + " ", + " TT ", + " TR ", + " LR ", + " BR ", + " ", + " ", + " " + } + }, + { + nudge_left_tris, + sizeof(nudge_left_tris[0]), + D3DFVF_XYZ | D3DFVF_DIFFUSE, + { + " ", + " TT ", + " LT ", + " LR ", + " LB ", + " ", + " ", + " " + } + }, + { + nudge_top_tris, + sizeof(nudge_top_tris[0]), + D3DFVF_XYZ | D3DFVF_DIFFUSE, + { + " ", + " LT ", + " LT ", + " LB ", + " LB ", + " ", + " ", + " " + } + }, + { + nudge_bottom_tris, + sizeof(nudge_bottom_tris[0]), + D3DFVF_XYZ | D3DFVF_DIFFUSE, + { + " ", + " ", + " LT ", + " Lt ", + " LB ", + " lB ", + " ", + " " + } + }, + { + center_tris_t, + sizeof(center_tris_t[0]), + D3DFVF_XYZRHW | D3DFVF_DIFFUSE, + { + " ", + " ", + " TT ", + " LR ", + " LR ", + " BB ", + " ", + " " + } + }, + { + edge_tris_t, + sizeof(edge_tris_t[0]), + D3DFVF_XYZRHW | D3DFVF_DIFFUSE, + { + " ", + " TT ", + " LT ", + " LR ", + " LB ", + " ", + " ", + " " + } + }, + }; + + window = create_window(); + d3d = Direct3DCreate9(D3D_SDK_VERSION); + ok(!!d3d, "Failed to create a D3D object.\n"); + + if (!(device = create_device(d3d, window, window, TRUE))) + { + skip("Failed to create a 3D device.\n"); + IDirect3D9_Release(d3d); + DestroyWindow(window); + return; + } + + hr = IDirect3DDevice9_CreateRenderTarget(device, vp_size, vp_size, + D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0, FALSE, &rt, NULL); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + hr = IDirect3DDevice9_GetBackBuffer(device, 0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + + hr = IDirect3DDevice9_GetDeviceCaps(device, &caps); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + if (caps.VertexShaderVersion >= D3DVS_VERSION(1, 1)) + { + hr = IDirect3DDevice9_CreateVertexShader(device, vs_code, &shader); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + } + else + skip("Skipping vertex shader codepath in filling convention test.\n"); + + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_LIGHTING, FALSE); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_ZENABLE, D3DZB_FALSE); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + hr = IDirect3DDevice9_SetFVF(device, tests[i].fvf); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + + /* Run tests with shader and fixed function vertex processing if shaders are + * supported. There's no point in running the XYZRHW tests with a VS though. */ + if (shader && ((tests[i].fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZ)) + j = 0; + else + j = 2; + + for (; j < 4; ++j) + { + cur = (j & 1) ? rt : backbuffer; + + hr = IDirect3DDevice9_SetVertexShader(device, (j & 2) ? NULL : shader); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + + hr = IDirect3DDevice9_SetRenderTarget(device, 0, cur); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET, colour_clear, 0.0f, 0); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + hr = IDirect3DDevice9_SetViewport(device, &vp); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + + hr = IDirect3DDevice9_BeginScene(device); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + hr = IDirect3DDevice9_DrawPrimitiveUP(device, D3DPT_TRIANGLELIST, 4, tests[i].geometry, tests[i].stride); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + hr = IDirect3DDevice9_EndScene(device); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + + get_rt_readback(cur, &rb); + for (y = 0; y < 8; y++) + { + for (x = 0; x < 8; x++) + { + todo = FALSE; + switch (tests[i].expected[y][x]) + { + case 'l': todo = TRUE; + case 'L': + expected = colour_left; + break; + case 't': todo = TRUE; + case 'T': + expected = colour_top; + break; + case 'r': todo = TRUE; + case 'R': + expected = colour_right; + break; + case 'b': todo = TRUE; + case 'B': + expected = colour_bottom; + break; + case ' ': + expected = colour_clear; + break; + default: + ok(0, "Unexpected entry in expected test char\n"); + expected = 0xdeadbeef; + } + colour = get_readback_color(&rb, x, y); + /* The nudge-to-bottom test fails on cards that give us a bottom-left + * filling convention. The cause isn't the bottom part of the filling + * convention, but because wined3d will nudge geometry to the left to + * keep diagonals (the 'R' in test case 'edge_tris') intact. */ + todo_wine_if(todo && !color_match(colour, expected, 1)) + ok(color_match(colour, expected, 1), "Got unexpected colour %08x, %ux%u, case %u, j %u.\n", + colour, x, y, i, j); + } + } + release_surface_readback(&rb); + + /* For debugging */ + if (cur != backbuffer) + { + hr = IDirect3DDevice9_StretchRect(device, rt, NULL, backbuffer, NULL, D3DTEXF_POINT); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + } + hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL); + ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr); + } + } + + if (shader) + IDirect3DVertexShader9_Release(shader); + IDirect3DSurface9_Release(backbuffer); + IDirect3DSurface9_Release(rt); + refcount = IDirect3DDevice9_Release(device); + ok(!refcount, "Device has %u references left.\n", refcount); + IDirect3D9_Release(d3d); + DestroyWindow(window); +} + START_TEST(visual) { D3DADAPTER_IDENTIFIER9 identifier; @@ -27168,4 +27654,5 @@ START_TEST(visual) test_alpha_to_coverage(); test_sample_mask(); test_dynamic_map_synchronization(); + test_filling_convention(); }
On Wed, 29 Sept 2021 at 21:43, Stefan Dösinger stefan@codeweavers.com wrote:
- /* This test data follows the examples in MSDN's
* "Rasterization Rules (Direct3D 9)" article at
* https://docs.microsoft.com/en-us/windows/win32/direct3d9/rasterization-rules */
MSDN URLs haven't been very stable over the long term; we've generally avoided including them in source code for that reason.
This allows test_fractional_viewports to pass regardless of GL capabilies. I don't think we can get rid of WINED3D_PIXEL_CENTER_INTEGER (and add +0.5 in the client libs) due to vpos in d3d9 shaders.
Signed-off-by: Stefan Dösinger stefan@codeweavers.com
---
Version 3: Properly assign gl_info->limits.viewport_subpixel_bits. v2 of the patch had a compile error...
Version 2: Make it depend on GL_VIEWPORT_SUBPIXEL_BITS instead of support for GL_ARB_viewport_array. Floor viewport coordinates passed to GL if we use GL_ARB_viewport_array but decide the subpixel support is insufficient. --- dlls/wined3d/adapter_gl.c | 3 +++ dlls/wined3d/adapter_vk.c | 1 + dlls/wined3d/state.c | 16 ++++++++++++++++ dlls/wined3d/wined3d_private.h | 14 +++++++++++--- 4 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/dlls/wined3d/adapter_gl.c b/dlls/wined3d/adapter_gl.c index 386d26828dc..d31632dd474 100644 --- a/dlls/wined3d/adapter_gl.c +++ b/dlls/wined3d/adapter_gl.c @@ -991,6 +991,7 @@ static void quirk_broken_arb_fog(struct wined3d_gl_info *gl_info)
static void quirk_broken_viewport_subpixel_bits(struct wined3d_gl_info *gl_info) { + gl_info->limits.viewport_subpixel_bits = 0; if (gl_info->supported[ARB_CLIP_CONTROL]) { TRACE("Disabling ARB_clip_control.\n"); @@ -3655,6 +3656,7 @@ static BOOL wined3d_adapter_init_gl_caps(struct wined3d_adapter *adapter, TRACE("Disabling ARB_clip_control because viewport subpixel bits < 8.\n"); gl_info->supported[ARB_CLIP_CONTROL] = FALSE; } + gl_info->limits.viewport_subpixel_bits = subpixel_bits; } if (gl_info->supported[ARB_CLIP_CONTROL] && !gl_info->supported[ARB_VIEWPORT_ARRAY]) { @@ -5133,6 +5135,7 @@ static void wined3d_adapter_gl_init_d3d_info(struct wined3d_adapter_gl *adapter_ d3d_info->full_ffp_varyings = !!(shader_caps.wined3d_caps & WINED3D_SHADER_CAP_FULL_FFP_VARYINGS); d3d_info->scaled_resolve = !!gl_info->supported[EXT_FRAMEBUFFER_MULTISAMPLE_BLIT_SCALED]; d3d_info->pbo = !!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]; + d3d_info->subpixel_viewport = gl_info->limits.viewport_subpixel_bits >= 8; d3d_info->feature_level = feature_level_from_caps(gl_info, &shader_caps, &fragment_caps); d3d_info->filling_convention_nudge = gl_info->filling_convention_nudge;
diff --git a/dlls/wined3d/adapter_vk.c b/dlls/wined3d/adapter_vk.c index 324f4316901..ed4559faec6 100644 --- a/dlls/wined3d/adapter_vk.c +++ b/dlls/wined3d/adapter_vk.c @@ -2205,6 +2205,7 @@ static void wined3d_adapter_vk_init_d3d_info(struct wined3d_adapter_vk *adapter_ d3d_info->scaled_resolve = false; d3d_info->pbo = true; d3d_info->feature_level = feature_level_from_caps(&shader_caps); + d3d_info->subpixel_viewport = true;
/* Like GL, Vulkan doesn't explicitly specify a filling convention and only mandates that a * shared edge of two adjacent triangles generate a fragment for exactly one of the triangles. diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c index 5c1c69fb650..85c80198ebf 100644 --- a/dlls/wined3d/state.c +++ b/dlls/wined3d/state.c @@ -4202,6 +4202,22 @@ static void viewport_miscpart(struct wined3d_context *context, const struct wine viewports[i * 4 + 1] = vp[i].y; viewports[i * 4 + 2] = vp[i].width; viewports[i * 4 + 3] = vp[i].height; + + /* Don't pass fractionals to GL if we earlier decided not to use + * this functionality for two reasons: First, GL might offer us + * fewer than 8 bits, and still make use of the fractional, in + * addition to the emulation we apply in shader_get_position_fixup. + * Second, even if GL tells us it has no subpixel precision (Mac OS!) + * it might still do something with the fractional amount, e.g. + * round it upwards. I can't find any info on rounding in + * GL_ARB_viewport_array. */ + if (!context->d3d_info->subpixel_viewport) + { + viewports[i * 4] = floor(viewports[i * 4]); + viewports[i * 4 + 1] = floor(viewports[i * 4 + 1]); + viewports[i * 4 + 2] = floor(viewports[i * 4 + 2]); + viewports[i * 4 + 3] = floor(viewports[i * 4 + 3]); + } }
if (context->viewport_count > state->viewport_count) diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 0990bffed21..6610f1333ae 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -240,6 +240,7 @@ struct wined3d_d3d_info uint32_t full_ffp_varyings : 1; uint32_t scaled_resolve : 1; uint32_t pbo : 1; + uint32_t subpixel_viewport : 1; enum wined3d_feature_level feature_level;
DWORD multisample_draw_location; @@ -3224,6 +3225,7 @@ struct wined3d_gl_limits
unsigned int framebuffer_width; unsigned int framebuffer_height; + unsigned int viewport_subpixel_bits;
UINT glsl_varyings; UINT glsl_vs_float_constants; @@ -5681,7 +5683,7 @@ static inline BOOL shader_is_scalar(const struct wined3d_shader_register *reg) static inline void shader_get_position_fixup(const struct wined3d_context *context, const struct wined3d_state *state, unsigned int fixup_count, float *position_fixup) { - float center_offset; + float center_offset, x = 0.0f, y = 0.0f; unsigned int i;
/* See get_projection_matrix() in utils.c for a discussion of the position fixup. @@ -5698,8 +5700,14 @@ static inline void shader_get_position_fixup(const struct wined3d_context *conte { position_fixup[4 * i ] = 1.0f; position_fixup[4 * i + 1] = 1.0f; - position_fixup[4 * i + 2] = center_offset / state->viewports[i].width; - position_fixup[4 * i + 3] = -center_offset / state->viewports[i].height; + if (!context->d3d_info->subpixel_viewport) + { + double dummy; + x = modf(state->viewports[i].x, &dummy) * 2.0f; + y = modf(state->viewports[i].y, &dummy) * 2.0f; + } + position_fixup[4 * i + 2] = (center_offset + x) / state->viewports[i].width; + position_fixup[4 * i + 3] = -(center_offset + y) / state->viewports[i].height;
if (context->render_offscreen) {