Wine-devel
Threads by month
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
September 2020
- 79 participants
- 771 discussions
[PATCH v2] xactengine3_7: Return a valid HRESULT in IXACT3SoundBank Play
by Alistair Leslie-Hughes 18 Sep '20
by Alistair Leslie-Hughes 18 Sep '20
18 Sep '20
Signed-off-by: Alistair Leslie-Hughes <leslie_alistair(a)hotmail.com>
---
dlls/xactengine3_7/xact_dll.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/dlls/xactengine3_7/xact_dll.c b/dlls/xactengine3_7/xact_dll.c
index 742fe86cc9..aa16f062f5 100644
--- a/dlls/xactengine3_7/xact_dll.c
+++ b/dlls/xactengine3_7/xact_dll.c
@@ -273,7 +273,7 @@ static HRESULT WINAPI IXACT3SoundBankImpl_Play(IXACT3SoundBank *iface,
XACT3SoundBankImpl *This = impl_from_IXACT3SoundBank(iface);
XACT3CueImpl *cue;
FACTCue *fcue;
- HRESULT hr;
+ UINT ret;
TRACE("(%p)->(%u, 0x%x, %u, %p)\n", This, nCueIndex, dwFlags, timeOffset,
ppCue);
@@ -283,13 +283,13 @@ static HRESULT WINAPI IXACT3SoundBankImpl_Play(IXACT3SoundBank *iface,
* -flibit
*/
if (ppCue == NULL){
- hr = FACTSoundBank_Play(This->fact_soundbank, nCueIndex, dwFlags,
+ ret = FACTSoundBank_Play(This->fact_soundbank, nCueIndex, dwFlags,
timeOffset, NULL);
}else{
- hr = FACTSoundBank_Play(This->fact_soundbank, nCueIndex, dwFlags,
+ ret = FACTSoundBank_Play(This->fact_soundbank, nCueIndex, dwFlags,
timeOffset, &fcue);
- if(FAILED(hr))
- return hr;
+ if(ret != 0)
+ goto done;
cue = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*cue));
if (!cue)
@@ -304,7 +304,11 @@ static HRESULT WINAPI IXACT3SoundBankImpl_Play(IXACT3SoundBank *iface,
*ppCue = (IXACT3Cue*)&cue->IXACT3Cue_iface;
}
- return hr;
+done:
+ if(ret != 0)
+ WARN("FACTSoundBank_Play returned %d\n", ret);
+
+ return !ret ? S_OK : E_FAIL;
}
static HRESULT WINAPI IXACT3SoundBankImpl_Stop(IXACT3SoundBank *iface,
--
2.28.0
1
0
18 Sep '20
Signed-off-by: Alistair Leslie-Hughes <leslie_alistair(a)hotmail.com>
---
dlls/dmstyle/style.c | 33 ++++++++++++++++++++++++++++++---
1 file changed, 30 insertions(+), 3 deletions(-)
diff --git a/dlls/dmstyle/style.c b/dlls/dmstyle/style.c
index 6eaedb1fbd..efa8d843af 100644
--- a/dlls/dmstyle/style.c
+++ b/dlls/dmstyle/style.c
@@ -137,9 +137,36 @@ static ULONG WINAPI IDirectMusicStyle8Impl_Release(IDirectMusicStyle8 *iface)
static HRESULT WINAPI IDirectMusicStyle8Impl_GetBand(IDirectMusicStyle8 *iface, WCHAR *pwszName,
IDirectMusicBand **ppBand)
{
- IDirectMusicStyle8Impl *This = impl_from_IDirectMusicStyle8(iface);
- FIXME("(%p, %p, %p): stub\n", This, pwszName, ppBand);
- return S_OK;
+ IDirectMusicStyle8Impl *This = impl_from_IDirectMusicStyle8(iface);
+ struct style_band *band;
+ HRESULT hr;
+
+ TRACE("(%p, %s, %p): stub\n", This, debugstr_w(pwszName), ppBand);
+
+ if (!pwszName)
+ return E_POINTER;
+
+ LIST_FOR_EACH_ENTRY(band, &This->bands, struct style_band, entry) {
+ IDirectMusicObject *obj;
+
+ hr = IDirectMusicBand_QueryInterface(band->pBand, &IID_IDirectMusicObject, (void**)&obj);
+ if (SUCCEEDED(hr)) {
+ DMUS_OBJECTDESC desc;
+
+ if (IDirectMusicObject_GetDescriptor(obj, &desc) == S_OK) {
+ if(desc.dwValidData & DMUS_OBJ_NAME && !lstrcmpW(pwszName, desc.wszName)) {
+ IDirectMusicObject_Release(obj);
+ IDirectMusicBand_AddRef(band->pBand);
+ *ppBand = band->pBand;
+ return S_OK;
+ }
+ }
+
+ IDirectMusicObject_Release(obj);
+ }
+ }
+
+ return S_FALSE;
}
static HRESULT WINAPI IDirectMusicStyle8Impl_EnumBand(IDirectMusicStyle8 *iface, DWORD dwIndex,
--
2.28.0
1
0
18 Sep '20
Signed-off-by: Alistair Leslie-Hughes <leslie_alistair(a)hotmail.com>
---
dlls/dmband/band.c | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c
index 901f5a69ee..c0268f439e 100644
--- a/dlls/dmband/band.c
+++ b/dlls/dmband/band.c
@@ -92,9 +92,25 @@ static ULONG WINAPI IDirectMusicBandImpl_Release(IDirectMusicBand *iface)
static HRESULT WINAPI IDirectMusicBandImpl_CreateSegment(IDirectMusicBand *iface,
IDirectMusicSegment **ppSegment)
{
- IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface);
- FIXME("(%p, %p): stub\n", This, ppSegment);
- return S_OK;
+ IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface);
+ HRESULT hr;
+ DMUS_BAND_PARAM bandparam;
+
+ FIXME("(%p, %p): stub\n", This, ppSegment);
+
+ hr = CoCreateInstance(&CLSID_DirectMusicSegment, NULL, CLSCTX_INPROC,
+ &IID_IDirectMusicSegment, (void**)ppSegment);
+ if (FAILED(hr))
+ return hr;
+
+ bandparam.mtTimePhysical = 0;
+ bandparam.pBand = &This->IDirectMusicBand_iface;
+ IDirectMusicBand_AddRef(bandparam.pBand);
+ hr = IDirectMusicSegment_SetParam(*ppSegment, &GUID_BandParam, 0xffffffff, DMUS_SEG_ALLTRACKS,
+ 0, &bandparam);
+ IDirectMusicBand_Release(bandparam.pBand);
+
+ return hr;
}
static HRESULT WINAPI IDirectMusicBandImpl_Download(IDirectMusicBand *iface,
--
2.28.0
1
0
18 Sep '20
From: Andrey Turkin <andrey.turkin(a)gmail.com>
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=12457
Signed-off-by: Gijs Vermeulen <gijsvrm(a)gmail.com>
---
Superseeds 192607
dlls/ieframe/tests/webbrowser.c | 18 ++++++++++++++++++
dlls/ieframe/webbrowser.c | 21 +++++++++++++++++++--
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/dlls/ieframe/tests/webbrowser.c b/dlls/ieframe/tests/webbrowser.c
index 9c2c393aaf..79f019312e 100644
--- a/dlls/ieframe/tests/webbrowser.c
+++ b/dlls/ieframe/tests/webbrowser.c
@@ -256,6 +256,22 @@ static void _test_LocationURL(unsigned line, IWebBrowser2 *wb, const WCHAR *exur
}
}
+#define test_LocationName(a,b) _test_LocationName(__LINE__,a,b)
+static void _test_LocationName(unsigned line, IWebBrowser2 *wb, const WCHAR *exname)
+{
+ BSTR name = (void*)0xdeadbeef;
+ HRESULT hres;
+
+ hres = IWebBrowser2_get_LocationName(wb, &name);
+ ok_(__FILE__,line) (hres == (*exname ? S_OK : S_FALSE), "get_LocationName failed: %08x\n", hres);
+todo_wine_if(!is_http && *exname)
+{
+ ok_(__FILE__,line) (!lstrcmpW(name, exname) || broken(is_http && !lstrcmpW(name, current_url)) /* Win10 2004 */,
+ "expected %s, got %s\n", wine_dbgstr_w(exname), wine_dbgstr_w(name));
+}
+ SysFreeString(name);
+}
+
#define test_ready_state(a,b) _test_ready_state(__LINE__,a,b)
static void _test_ready_state(unsigned line, READYSTATE exstate, VARIANT_BOOL expect_busy)
{
@@ -2791,11 +2807,13 @@ static void test_ConnectionPoint(IWebBrowser2 *unk, BOOL init)
static void test_Navigate2(IWebBrowser2 *webbrowser, const WCHAR *nav_url)
{
+ const WCHAR *title = L"WineHQ - Run Windows applications on Linux, BSD, Solaris and Mac OS X";
VARIANT url;
BOOL is_file;
HRESULT hres;
test_LocationURL(webbrowser, is_first_load ? L"" : current_url);
+ test_LocationName(webbrowser, is_first_load ? L"" : (is_http ? title : current_url));
test_ready_state(is_first_load ? READYSTATE_UNINITIALIZED : READYSTATE_COMPLETE, VARIANT_FALSE);
is_http = !memcmp(nav_url, "http:", 5);
diff --git a/dlls/ieframe/webbrowser.c b/dlls/ieframe/webbrowser.c
index 87b74b8e01..965cd5f0ee 100644
--- a/dlls/ieframe/webbrowser.c
+++ b/dlls/ieframe/webbrowser.c
@@ -539,8 +539,25 @@ static HRESULT WINAPI WebBrowser_put_Height(IWebBrowser2 *iface, LONG Height)
static HRESULT WINAPI WebBrowser_get_LocationName(IWebBrowser2 *iface, BSTR *LocationName)
{
WebBrowser *This = impl_from_IWebBrowser2(iface);
- FIXME("(%p)->(%p)\n", This, LocationName);
- return E_NOTIMPL;
+ IHTMLDocument2 *doc;
+ HRESULT hres;
+
+ TRACE("(%p)->(%p)\n", This, LocationName);
+
+ if(This->doc_host.document &&
+ SUCCEEDED(IUnknown_QueryInterface(This->doc_host.document, &IID_IHTMLDocument2, (void **)&doc))) {
+ hres = IHTMLDocument2_get_title(doc, LocationName);
+ IHTMLDocument2_Release(doc);
+ } else if(This->doc_host.url) {
+ *LocationName = SysAllocString(This->doc_host.url);
+ hres = S_OK;
+ } else {
+ static const WCHAR null_char = 0;
+ *LocationName = SysAllocString(&null_char);
+ hres = S_FALSE;
+ }
+
+ return hres;
}
static HRESULT WINAPI WebBrowser_get_LocationURL(IWebBrowser2 *iface, BSTR *LocationURL)
--
2.28.0
2
1
From: Matthew Wong <itsmattkc(a)gmail.com>
Implement functions used by some games (notably LEGO Island) for
determining which 3D object in a scene was clicked by the mouse cursor.
Fighting Steel also uses this function for mouse over. Previous stubs
would cause LEGO Island to crash upon any click and Fighting Steel
to crash on game start. A patch posted years ago on the bug thread
provided the minimum functionality to prevent crashes, but still
rendered large portions of the game inaccessible without them
implemented correctly.
Picking has been implemented by adding a "pick mode" in
d3d_execute_buffer_execute() which skips any drawing functions
leaving just the vertex processing. Adds click tests for each triangle
when in pick mode for creating an array of D3DPICKRECORDs.
Stress testing reveals this patch's Pick() implementation may have
slight inaccuracies to the original function; occasionally pixels right
on triangle edges result in successful picks when they don't with the
original function (and vice versa). It may be some sort of floating
point rounding error or other algorithm difference that would be
difficult to determine without seeing the original code. In practice, I
believe this inaccuracy is so negligible that it won't produce any
undesirable results for the user.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=10729
Signed-off-by: Matthew Wong <itsmattkc(a)gmail.com>
Signed-off-by: Myah Caron <qsniyg(a)protonmail.com>
---
v5: Don't set device->pick_record_count = 0 in GetPickRecords, confirming Stefan's comment.
Matthew's original patch is available here: https://www.winehq.org/pipermail/wine-devel/2019-August/149695.html.
This version addresses the corrections Stefan pointed out here: https://www.winehq.org/pipermail/wine-devel/2019-November/154023.html.
I've removed the "Add a" paragraphs from the commit message, primarily because the first one is now inaccurate due to the corrections.
One comment I have (purposefully) not addressed is the last one referring to the corner case. This is because I know next to nothing
about ddraw, and I don't have a testing machine on-hand either (only a VM). This also means that the new tests I have added are entirely
untested on a native machine, so I'll have to rely on Marvin.
I also have not addressed the WINED3D_MAP_WRITE comment, as if WINED3D_MAP_READ is used, most tests fail with the following error:
warn:d3d:wined3d_resource_map Resource does not have MAP_R access.
Regarding this comment:
>The other question - to be answered by extending the test - is which
>count is used here. Should you copy all pick records in the device, or
>*count pick records, or whichever is smaller, or return an error if
>*count < device->pick_record_count.
I have added rec_count = 0 for the following to explicitly test for this:
rec_count = 0;
hr = IDirect3DDevice_GetPickRecords(device, &rec_count, NULL);
However, since rec_count should have already been set to 0 if the previous GetPickRecords test passed, I believe this means that the
inputted count variable is ignored by GetPickRecords.
dlls/ddraw/ddraw_private.h | 7 +-
dlls/ddraw/device.c | 65 +++++++++++--
dlls/ddraw/executebuffer.c | 194 +++++++++++++++++++++++++++++++++++--
dlls/ddraw/tests/ddraw1.c | 131 +++++++++++++++++++++++++
4 files changed, 381 insertions(+), 16 deletions(-)
diff --git a/dlls/ddraw/ddraw_private.h b/dlls/ddraw/ddraw_private.h
index 776f61e1024..9d32aa61b1a 100644
--- a/dlls/ddraw/ddraw_private.h
+++ b/dlls/ddraw/ddraw_private.h
@@ -335,6 +335,11 @@ struct d3d_device
struct d3d_viewport *current_viewport;
D3DVIEWPORT7 active_viewport;
+ /* Pick data */
+ D3DPICKRECORD *pick_records;
+ DWORD pick_record_count;
+ DWORD pick_record_size;
+
/* Required to keep track which of two available texture blending modes in d3ddevice3 is used */
BOOL legacyTextureBlending;
D3DTEXTUREBLEND texture_map_blend;
@@ -568,7 +573,7 @@ struct d3d_execute_buffer *unsafe_impl_from_IDirect3DExecuteBuffer(IDirect3DExec
/* The execute function */
HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *execute_buffer,
- struct d3d_device *device) DECLSPEC_HIDDEN;
+ struct d3d_device *device, D3DRECT *pick_rect) DECLSPEC_HIDDEN;
/*****************************************************************************
* IDirect3DVertexBuffer
diff --git a/dlls/ddraw/device.c b/dlls/ddraw/device.c
index 3f1fe08c11e..20465542415 100644
--- a/dlls/ddraw/device.c
+++ b/dlls/ddraw/device.c
@@ -350,6 +350,9 @@ static ULONG WINAPI d3d_device_inner_Release(IUnknown *iface)
IDirect3DDevice3_DeleteViewport(&This->IDirect3DDevice3_iface, &vp->IDirect3DViewport3_iface);
}
+ if (This->pick_record_size > 0)
+ heap_free(This->pick_records);
+
TRACE("Releasing render target %p.\n", This->rt_iface);
rt_iface = This->rt_iface;
This->rt_iface = NULL;
@@ -759,7 +762,7 @@ static HRESULT WINAPI d3d_device1_Execute(IDirect3DDevice *iface,
/* Execute... */
wined3d_mutex_lock();
- hr = d3d_execute_buffer_execute(buffer, device);
+ hr = d3d_execute_buffer_execute(buffer, device, NULL);
wined3d_mutex_unlock();
return hr;
@@ -1026,16 +1029,44 @@ static HRESULT WINAPI d3d_device1_NextViewport(IDirect3DDevice *iface,
* x2 and y2 are ignored.
*
* Returns:
- * D3D_OK because it's a stub
+ * D3D_OK on success
+ * DDERR_INVALIDPARAMS if any of the parameters == NULL
*
*****************************************************************************/
static HRESULT WINAPI d3d_device1_Pick(IDirect3DDevice *iface, IDirect3DExecuteBuffer *buffer,
IDirect3DViewport *viewport, DWORD flags, D3DRECT *rect)
{
- FIXME("iface %p, buffer %p, viewport %p, flags %#x, rect %s stub!\n",
+ struct d3d_device *device = impl_from_IDirect3DDevice(iface);
+ struct d3d_execute_buffer *buffer_impl = unsafe_impl_from_IDirect3DExecuteBuffer(buffer);
+ struct d3d_viewport *viewport_impl = unsafe_impl_from_IDirect3DViewport(viewport);
+ HRESULT hr;
+
+ TRACE("iface %p, buffer %p, viewport %p, flags %#x, rect %s.\n",
iface, buffer, viewport, flags, wine_dbgstr_rect((RECT *)rect));
- return D3D_OK;
+ /* Sanity checks */
+ if (!buffer)
+ {
+ WARN("NULL buffer, returning DDERR_INVALIDPARAMS\n");
+ return DDERR_INVALIDPARAMS;
+ }
+
+ if (!viewport)
+ {
+ WARN("NULL viewport, returning DDERR_INVALIDPARAMS\n");
+ return DDERR_INVALIDPARAMS;
+ }
+
+ if (FAILED(hr = IDirect3DDevice3_SetCurrentViewport
+ (&device->IDirect3DDevice3_iface, &viewport_impl->IDirect3DViewport3_iface)))
+ return hr;
+
+ /* Execute the pick */
+ wined3d_mutex_lock();
+ hr = d3d_execute_buffer_execute(buffer_impl, device, rect);
+ wined3d_mutex_unlock();
+
+ return hr;
}
/*****************************************************************************
@@ -1051,13 +1082,35 @@ static HRESULT WINAPI d3d_device1_Pick(IDirect3DDevice *iface, IDirect3DExecuteB
* D3DPickRec: Address to store the resulting D3DPICKRECORD array.
*
* Returns:
- * D3D_OK, because it's a stub
+ * D3D_OK always
*
*****************************************************************************/
static HRESULT WINAPI d3d_device1_GetPickRecords(IDirect3DDevice *iface,
DWORD *count, D3DPICKRECORD *records)
{
- FIXME("iface %p, count %p, records %p stub!\n", iface, count, records);
+ struct d3d_device *device;
+
+ TRACE("iface %p, count %p, records %p.\n", iface, count, records);
+
+ /* Windows doesn't check if count is non-NULL */
+
+ wined3d_mutex_lock();
+
+ device = impl_from_IDirect3DDevice(iface);
+
+ /* Set count to the number of pick records we have */
+ *count = device->pick_record_count;
+
+ /* It is correct usage according to documentation to call this function with records == NULL
+ to retrieve _just_ the record count, which the caller can then use to allocate an
+ appropriately sized array, then call this function again to fill that array with data. */
+ if (records && count)
+ {
+ /* If we have a destination array and records to copy, copy them now */
+ memcpy(records, device->pick_records, sizeof(*device->pick_records) * device->pick_record_count);
+ }
+
+ wined3d_mutex_unlock();
return D3D_OK;
}
diff --git a/dlls/ddraw/executebuffer.c b/dlls/ddraw/executebuffer.c
index e0a9445bf71..f5116573e46 100644
--- a/dlls/ddraw/executebuffer.c
+++ b/dlls/ddraw/executebuffer.c
@@ -45,15 +45,106 @@ static void _dump_D3DEXECUTEBUFFERDESC(const D3DEXECUTEBUFFERDESC *lpDesc) {
TRACE("lpData : %p\n", lpDesc->lpData);
}
-HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d_device *device)
+#define TRIANGLE_SIZE 3
+/*****************************************************************************
+ * d3d_execute_buffer_pick_test
+ *
+ * Determines whether a "point" is inside a "triangle". Mainly used when
+ * executing a "pick" from an execute buffer to determine whether a pixel
+ * coordinate (often a mouse coordinate) is inside a triangle (and
+ * therefore clicking or hovering over a 3D object in the scene). This
+ * function uses triangle rasterization algorithms to determine if the
+ * pixel falls inside (using the top-left rule, in accordance with
+ * documentation).
+ *
+ * Params:
+ * x: The X coordinate of the point to verify.
+ * y: The Y coordinate of the point to verify.
+ * verts: An array of vertices describing the screen coordinates of the
+ * triangle. This function expects 3 elements in this array.
+ *
+ * Returns:
+ * TRUE if the pixel coordinate is inside this triangle
+ * FALSE if not
+ *
+ *****************************************************************************/
+static BOOL d3d_execute_buffer_pick_test(LONG x, LONG y, D3DTLVERTEX* verts)
+{
+ UINT i;
+
+ for (i = 0; i < TRIANGLE_SIZE; i++)
+ {
+ D3DTLVERTEX* v1 = &verts[(i) % TRIANGLE_SIZE];
+ D3DTLVERTEX* v2 = &verts[(i + 1) % TRIANGLE_SIZE];
+ D3DVALUE bias = 0.0f;
+
+ /* Edge function - determines whether pixel is inside triangle */
+ D3DVALUE w = (v2->u1.sx - v1->u1.sx) * (y - v1->u2.sy) - (v2->u2.sy - v1->u2.sy) * (x - v1->u1.sx);
+
+ /* Force top-left rule */
+ if ((v1->u2.sy == v2->u2.sy && v1->u1.sx > v2->u1.sx) || (v1->u2.sy < v2->u2.sy))
+ bias = 1.0f;
+
+ if (w < bias)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************
+ * d3d_execute_buffer_z_value_at_coords
+ *
+ * Returns the Z point of a triangle given an X, Y coordinate somewhere inside
+ * the triangle. Used as the `dvZ` parameter of D3DPICKRECORD.
+ *
+ * Params:
+ * x: The X coordinate of the point to verify.
+ * y: The Y coordinate of the point to verify.
+ * verts: An array of vertices describing the screen coordinates of the
+ * triangle. This function expects 3 elements in this array.
+ *
+ * Returns:
+ * A floating-point Z value that can be used directly as the dvZ member of a
+ * D3DPICKRECORD.
+ *
+ *****************************************************************************/
+static D3DVALUE d3d_execute_buffer_z_value_at_coords(LONG x, LONG y, D3DTLVERTEX* verts)
+{
+ UINT i;
+
+ D3DVALUE z1 = 0;
+ D3DVALUE z2 = 0;
+
+ for (i = 0; i < TRIANGLE_SIZE; i++)
+ {
+ D3DTLVERTEX* v1 = &verts[i];
+ D3DTLVERTEX* v2 = &verts[(i + 1) % TRIANGLE_SIZE];
+ D3DTLVERTEX* v3 = &verts[(i + 2) % TRIANGLE_SIZE];
+
+ z1 += v3->u3.sz * (x - v1->u1.sx) * (y - v2->u2.sy) - v2->u3.sz * (x - v1->u1.sx) * (y - v3->u2.sy);
+ z2 += (x - v1->u1.sx) * (y - v2->u2.sy) - (x - v1->u1.sx) * (y - v3->u2.sy);
+ }
+
+ return z1 / z2;
+}
+
+HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d_device *device,
+ D3DRECT* pick_rect)
{
DWORD is = buffer->data.dwInstructionOffset;
char *instr = (char *)buffer->desc.lpData + is;
unsigned int i, primitive_size;
- struct wined3d_map_desc map_desc;
+ struct wined3d_map_desc map_desc, vert_map_desc;
struct wined3d_box box = {0};
HRESULT hr;
+ /* Variables used for picking */
+ const unsigned int vertex_size = get_flexible_vertex_size(D3DFVF_TLVERTEX);
+ D3DTLVERTEX verts[TRIANGLE_SIZE];
+
+ device->pick_record_count = 0;
+
TRACE("ExecuteData :\n");
if (TRACE_ON(ddraw))
_dump_executedata(&(buffer->data));
@@ -69,6 +160,26 @@ HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d
instr += sizeof(*current);
primitive_size = 0;
+ if (pick_rect != NULL)
+ {
+ switch (current->bOpcode)
+ {
+ /* None of these opcodes seem to be necessary for picking */
+ case D3DOP_POINT:
+ case D3DOP_LINE:
+ case D3DOP_STATETRANSFORM:
+ case D3DOP_STATELIGHT:
+ case D3DOP_STATERENDER:
+ case D3DOP_TEXTURELOAD:
+ case D3DOP_SPAN:
+ FIXME("ignoring opcode %d for picking\n", current->bOpcode);
+ instr += count * size;
+ continue;
+ default:
+ break;
+ }
+ }
+
switch (current->bOpcode)
{
case D3DOP_POINT:
@@ -173,6 +284,66 @@ HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d
{
case 3:
indices[(i * primitive_size) + 2] = ci->u3.v3;
+
+ if (pick_rect != NULL) {
+ UINT j;
+
+ /* Get D3DTLVERTEX objects for each triangle vertex */
+ for (j = 0; j < TRIANGLE_SIZE; j++) {
+
+ /* Get index of vertex from D3DTRIANGLE struct */
+ switch (j) {
+ case 0: box.left = vertex_size * ci->u1.v1; break;
+ case 1: box.left = vertex_size * ci->u2.v2; break;
+ case 2: box.left = vertex_size * ci->u3.v3; break;
+ }
+
+ box.right = box.left + vertex_size;
+ if (FAILED(hr = wined3d_resource_map(wined3d_buffer_get_resource(buffer->dst_vertex_buffer),
+ 0, &vert_map_desc, &box, WINED3D_MAP_WRITE))) {
+ return hr;
+ } else {
+ /* Copy vert data into stack array */
+ verts[j] = *((D3DTLVERTEX*)vert_map_desc.data);
+
+ wined3d_resource_unmap(wined3d_buffer_get_resource(buffer->dst_vertex_buffer), 0);
+ }
+ }
+
+ /* Use vertices acquired above to test for clicking */
+ if (d3d_execute_buffer_pick_test(pick_rect->u1.x1, pick_rect->u2.y1, verts))
+ {
+ D3DPICKRECORD* record;
+
+ device->pick_record_count++;
+
+ /* Grow the array if necessary */
+ if (device->pick_record_count > device->pick_record_size)
+ {
+ if (device->pick_record_size == 0) device->pick_record_size = 1;
+ device->pick_record_size *= 2;
+ device->pick_records = heap_realloc(device->pick_records,
+ sizeof(*device->pick_records) * device->pick_record_size);
+ }
+
+ /* Fill record parameters */
+ record = &device->pick_records[device->pick_record_count - 1];
+
+ record->bOpcode = current->bOpcode;
+ record->bPad = 0;
+
+ /* Write current instruction offset into file */
+ record->dwOffset = (DWORD)instr - (DWORD)buffer->desc.lpData - is;
+
+ /* Formula for returning the Z value at this X/Y */
+ record->dvZ = d3d_execute_buffer_z_value_at_coords(pick_rect->u1.x1, pick_rect->u2.y1, verts);
+
+ /* We have a successful pick so we can skip the rest of the triangles */
+ instr += size * (count - i - 1);
+ count = i;
+ }
+ }
+
/* Drop through. */
case 2:
indices[(i * primitive_size) + 1] = ci->u2.v2;
@@ -183,13 +354,17 @@ HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d
wined3d_resource_unmap(wined3d_buffer_get_resource(buffer->index_buffer), 0);
- wined3d_stateblock_set_stream_source(device->state, 0,
- buffer->dst_vertex_buffer, 0, sizeof(D3DTLVERTEX));
- wined3d_stateblock_set_vertex_declaration(device->state,
- ddraw_find_decl(device->ddraw, D3DFVF_TLVERTEX));
- wined3d_stateblock_set_index_buffer(device->state, buffer->index_buffer, WINED3DFMT_R16_UINT);
- wined3d_device_apply_stateblock(device->wined3d_device, device->state);
- wined3d_device_draw_indexed_primitive(device->wined3d_device, index_pos, index_count);
+ /* Skip drawing if we're picking */
+ if (pick_rect == NULL)
+ {
+ wined3d_stateblock_set_stream_source(device->state, 0,
+ buffer->dst_vertex_buffer, 0, sizeof(D3DTLVERTEX));
+ wined3d_stateblock_set_vertex_declaration(device->state,
+ ddraw_find_decl(device->ddraw, D3DFVF_TLVERTEX));
+ wined3d_stateblock_set_index_buffer(device->state, buffer->index_buffer, WINED3DFMT_R16_UINT);
+ wined3d_device_apply_stateblock(device->wined3d_device, device->state);
+ wined3d_device_draw_indexed_primitive(device->wined3d_device, index_pos, index_count);
+ }
buffer->index_pos = index_pos + index_count;
break;
@@ -423,6 +598,7 @@ HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d
end_of_buffer:
return D3D_OK;
}
+#undef TRIANGLE_SIZE
static inline struct d3d_execute_buffer *impl_from_IDirect3DExecuteBuffer(IDirect3DExecuteBuffer *iface)
{
diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c
index 2fac4d4b0ba..f6871e647d3 100644
--- a/dlls/ddraw/tests/ddraw1.c
+++ b/dlls/ddraw/tests/ddraw1.c
@@ -13880,6 +13880,136 @@ static void test_get_display_mode(void)
IDirectDraw_Release(ddraw);
}
+static void test_pick(void)
+{
+ static D3DTLVERTEX tquad[] =
+ {
+ {{320.0f}, {480.0f}, { 1.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}},
+ {{ 0.0f}, {480.0f}, {-0.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}},
+ {{320.0f}, { 0.0f}, { 1.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}},
+ {{ 0.0f}, { 0.0f}, {-0.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}},
+ };
+ IDirect3DExecuteBuffer *execute_buffer;
+ D3DEXECUTEBUFFERDESC exec_desc;
+ IDirect3DViewport *viewport;
+ IDirect3DDevice *device;
+ IDirectDraw *ddraw;
+ UINT inst_length;
+ HWND window;
+ HRESULT hr;
+ void *ptr;
+ DWORD rec_count;
+ D3DRECT pick_rect;
+ UINT screen_width = 640;
+ UINT screen_height = 480;
+ UINT hits = 0;
+ UINT nohits = 0;
+ int i, j;
+
+ window = create_window();
+ ddraw = create_ddraw();
+ ok(!!ddraw, "Failed to create a ddraw object.\n");
+ if (!(device = create_device(ddraw, window, DDSCL_NORMAL)))
+ {
+ skip("Failed to create a 3D device, skipping test.\n");
+ IDirectDraw_Release(ddraw);
+ DestroyWindow(window);
+ return;
+ }
+
+ viewport = create_viewport(device, 0, 0, screen_width, screen_height);
+
+ memset(&exec_desc, 0, sizeof(exec_desc));
+ exec_desc.dwSize = sizeof(exec_desc);
+ exec_desc.dwFlags = D3DDEB_BUFSIZE | D3DDEB_CAPS;
+ exec_desc.dwBufferSize = 1024;
+ exec_desc.dwCaps = D3DDEBCAPS_SYSTEMMEMORY;
+
+ hr = IDirect3DDevice_CreateExecuteBuffer(device, &exec_desc, &execute_buffer, NULL);
+ ok(SUCCEEDED(hr), "Failed to create execute buffer, hr %#x.\n", hr);
+ hr = IDirect3DExecuteBuffer_Lock(execute_buffer, &exec_desc);
+ ok(SUCCEEDED(hr), "Failed to lock execute buffer, hr %#x.\n", hr);
+ memcpy(exec_desc.lpData, tquad, sizeof(tquad));
+ ptr = ((BYTE *)exec_desc.lpData) + sizeof(tquad);
+ emit_process_vertices(&ptr, D3DPROCESSVERTICES_COPY, 0, 4);
+ emit_tquad(&ptr, 0);
+ emit_end(&ptr);
+ inst_length = (BYTE *)ptr - (BYTE *)exec_desc.lpData;
+ inst_length -= sizeof(tquad);
+ hr = IDirect3DExecuteBuffer_Unlock(execute_buffer);
+ ok(SUCCEEDED(hr), "Failed to unlock execute buffer, hr %#x.\n", hr);
+
+ set_execute_data(execute_buffer, 4, sizeof(tquad), inst_length);
+
+ /* Perform a number of picks, we should have a specific amount by the end */
+ for (i = 0; i < screen_width; i += 80)
+ {
+ for (j = 0; j < screen_height; j += 60)
+ {
+ pick_rect.x1 = i;
+ pick_rect.y1 = j;
+
+ hr = IDirect3DDevice_Pick(device, execute_buffer, viewport, 0, &pick_rect);
+ ok(SUCCEEDED(hr), "Failed to perform pick, hr %#x.\n", hr);
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, NULL);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ if (rec_count > 0)
+ hits++;
+ else
+ nohits++;
+ }
+ }
+
+ /*
+ We should have gotten precisely equal numbers of hits and no hits since our quad
+ covers exactly half the screen
+ */
+ ok(hits == nohits, "Got a non-equal amount of pick successes/failures: %i vs %i.\n", hits, nohits);
+
+ /* Try some specific pixel picks */
+ pick_rect.x1 = 480;
+ pick_rect.y1 = 360;
+ hr = IDirect3DDevice_Pick(device, execute_buffer, viewport, 0, &pick_rect);
+ ok(SUCCEEDED(hr), "Failed to perform pick, hr %#x.\n", hr);
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, NULL);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ ok(rec_count == 0, "Got incorrect number of pick records (expected 0): %i.\n", rec_count);
+
+ pick_rect.x1 = 240;
+ pick_rect.y1 = 120;
+ hr = IDirect3DDevice_Pick(device, execute_buffer, viewport, 0, &pick_rect);
+ ok(SUCCEEDED(hr), "Failed to perform pick, hr %#x.\n", hr);
+ rec_count = 0;
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, NULL);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ ok(rec_count == 1, "Got incorrect number of pick records (expected 1): %i.\n", rec_count);
+
+ if (rec_count == 1)
+ {
+ D3DPICKRECORD record;
+
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, &record);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ ok(rec_count == 1, "Got incorrect number of pick records (expected 1): %i.\n", rec_count);
+
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, &record);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ ok(rec_count == 1, "Got incorrect number of pick records (expected 1): %i.\n", rec_count);
+
+ /* Tests D3DPICKRECORD for correct information */
+ ok(record.bOpcode == 3, "Got incorrect bOpcode: %i.\n", record.bOpcode);
+ ok(record.bPad == 0, "Got incorrect bPad: %i.\n", record.bPad);
+ ok(record.dwOffset == 24, "Got incorrect dwOffset: %i.\n", record.dwOffset);
+ ok(compare_float(record.dvZ, 1.0, 0.1), "Got incorrect dvZ: %f.\n", record.dvZ);
+ }
+
+ destroy_viewport(device, viewport);
+ IDirect3DExecuteBuffer_Release(execute_buffer);
+ IDirect3DDevice_Release(device);
+ IDirectDraw_Release(ddraw);
+ DestroyWindow(window);
+}
+
START_TEST(ddraw1)
{
DDDEVICEIDENTIFIER identifier;
@@ -13996,4 +14126,5 @@ START_TEST(ddraw1)
test_vtbl_protection();
test_window_position();
test_get_display_mode();
+ test_pick();
}
--
2.28.0
2
1
18 Sep '20
calls with stack_size > 16 pop argument values into x3, overwriting
the number of 16-byte stack octwords. This breaks the checks for popping
x4..x7 and potentially unbalances sp (based on the vaue of args[16..23]).
Use a scratch register (x9) for this count so its lifetime does not
conflict with preparing the parameter/result registers.
Signed-off-by: Kevin Puetz <PuetzKevinA(a)JohnDeere.com>
---
dlls/rpcrt4/ndr_stubless.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/dlls/rpcrt4/ndr_stubless.c b/dlls/rpcrt4/ndr_stubless.c
index 9f579fcfd4..43debcb91d 100644
--- a/dlls/rpcrt4/ndr_stubless.c
+++ b/dlls/rpcrt4/ndr_stubless.c
@@ -1180,24 +1180,24 @@ LONG_PTR __cdecl call_server_func(SERVER_ROUTINE func, unsigned char *args, unsi
__ASM_GLOBAL_FUNC( call_server_func,
"stp x29, x30, [sp, #-16]!\n\t"
"mov x29, sp\n\t"
- "add x3, x2, #15\n\t"
- "lsr x3, x3, #4\n\t"
- "sub sp, sp, x3, lsl #4\n\t"
+ "add x9, x2, #15\n\t"
+ "lsr x9, x9, #4\n\t"
+ "sub sp, sp, x9, lsl #4\n\t"
"cbz x2, 2f\n"
"1:\tsub x2, x2, #8\n\t"
"ldr x4, [x1, x2]\n\t"
"str x4, [sp, x2]\n\t"
"cbnz x2, 1b\n"
"2:\tmov x8, x0\n\t"
- "cbz x3, 3f\n\t"
+ "cbz x9, 3f\n\t"
"ldp x0, x1, [sp], #16\n\t"
- "cmp x3, #1\n\t"
+ "cmp x9, #1\n\t"
"b.le 3f\n\t"
"ldp x2, x3, [sp], #16\n\t"
- "cmp x3, #2\n\t"
+ "cmp x9, #2\n\t"
"b.le 3f\n\t"
"ldp x4, x5, [sp], #16\n\t"
- "cmp x3, #3\n\t"
+ "cmp x9, #3\n\t"
"b.le 3f\n\t"
"ldp x6, x7, [sp], #16\n"
"3:\tblr x8\n\t"
1
0
>From 2d32f46c241079fa449a24dcf6f255d59fe1cbe5 Mon Sep 17 00:00:00 2001
From: Myah Caron <qsniyg(a)protonmail.com>
Date: Thu, 17 Sep 2020 13:53:38 -0700
Subject: [PATCH v4] ddraw: Implement Pick() and GetPickRecords().
From: Matthew Wong <itsmattkc(a)gmail.com>
Implement functions used by some games (notably LEGO Island) for
determining which 3D object in a scene was clicked by the mouse cursor.
Fighting Steel also uses this function for mouse over. Previous stubs
would cause LEGO Island to crash upon any click and Fighting Steel
to crash on game start. A patch posted years ago on the bug thread
provided the minimum functionality to prevent crashes, but still
rendered large portions of the game inaccessible without them
implemented correctly.
Picking has been implemented by adding a "pick mode" in
d3d_execute_buffer_execute() which skips any drawing functions
leaving just the vertex processing. Adds click tests for each triangle
when in pick mode for creating an array of D3DPICKRECORDs.
Stress testing reveals this patch's Pick() implementation may have
slight inaccuracies to the original function; occasionally pixels right
on triangle edges result in successful picks when they don't with the
original function (and vice versa). It may be some sort of floating
point rounding error or other algorithm difference that would be
difficult to determine without seeing the original code. In practice, I
believe this inaccuracy is so negligible that it won't produce any
undesirable results for the user.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=10729
Signed-off-by: Matthew Wong <itsmattkc(a)gmail.com>
Signed-off-by: Myah Caron <qsniyg(a)protonmail.com>
---
v4: Don't check "count" variable as windows doesn't do this (according to tests)
Matthew's original patch is available here: https://www.winehq.org/pipermail/wine-devel/2019-August/149695.html.
This version addresses the corrections Stefan pointed out here: https://www.winehq.org/pipermail/wine-devel/2019-November/154023.html.
I've removed the "Add a" paragraphs from the commit message, primarily because the first one is now inaccurate due to the corrections.
The only comment I have (purposefully) not addressed is the last one referring to the corner case. This is because I know next to nothing
about ddraw, and I don't have a testing machine on-hand either (only a VM). This also means that the new tests I have added are entirely
untested on a native machine, so I'll have to rely on Marvin.
Regarding this comment:
>The other question - to be answered by extending the test - is which
>count is used here. Should you copy all pick records in the device, or
>*count pick records, or whichever is smaller, or return an error if
>*count < device->pick_record_count.
I have added rec_count = 0 for the following to explicitly test for this:
rec_count = 0;
hr = IDirect3DDevice_GetPickRecords(device, &rec_count, NULL);
However, since rec_count should have already been set to 0 if the previous GetPickRecords test passed, I believe this means that the
inputted count variable is ignored by GetPickRecords.
dlls/ddraw/ddraw_private.h | 7 +-
dlls/ddraw/device.c | 68 +++++++++++--
dlls/ddraw/executebuffer.c | 194 +++++++++++++++++++++++++++++++++++--
dlls/ddraw/tests/ddraw1.c | 131 +++++++++++++++++++++++++
4 files changed, 384 insertions(+), 16 deletions(-)
diff --git a/dlls/ddraw/ddraw_private.h b/dlls/ddraw/ddraw_private.h
index 776f61e1024..9d32aa61b1a 100644
--- a/dlls/ddraw/ddraw_private.h
+++ b/dlls/ddraw/ddraw_private.h
@@ -335,6 +335,11 @@ struct d3d_device
struct d3d_viewport *current_viewport;
D3DVIEWPORT7 active_viewport;
+ /* Pick data */
+ D3DPICKRECORD *pick_records;
+ DWORD pick_record_count;
+ DWORD pick_record_size;
+
/* Required to keep track which of two available texture blending modes in d3ddevice3 is used */
BOOL legacyTextureBlending;
D3DTEXTUREBLEND texture_map_blend;
@@ -568,7 +573,7 @@ struct d3d_execute_buffer *unsafe_impl_from_IDirect3DExecuteBuffer(IDirect3DExec
/* The execute function */
HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *execute_buffer,
- struct d3d_device *device) DECLSPEC_HIDDEN;
+ struct d3d_device *device, D3DRECT *pick_rect) DECLSPEC_HIDDEN;
/*****************************************************************************
* IDirect3DVertexBuffer
diff --git a/dlls/ddraw/device.c b/dlls/ddraw/device.c
index 3f1fe08c11e..93c6e879e60 100644
--- a/dlls/ddraw/device.c
+++ b/dlls/ddraw/device.c
@@ -350,6 +350,9 @@ static ULONG WINAPI d3d_device_inner_Release(IUnknown *iface)
IDirect3DDevice3_DeleteViewport(&This->IDirect3DDevice3_iface, &vp->IDirect3DViewport3_iface);
}
+ if (This->pick_record_size > 0)
+ heap_free(This->pick_records);
+
TRACE("Releasing render target %p.\n", This->rt_iface);
rt_iface = This->rt_iface;
This->rt_iface = NULL;
@@ -759,7 +762,7 @@ static HRESULT WINAPI d3d_device1_Execute(IDirect3DDevice *iface,
/* Execute... */
wined3d_mutex_lock();
- hr = d3d_execute_buffer_execute(buffer, device);
+ hr = d3d_execute_buffer_execute(buffer, device, NULL);
wined3d_mutex_unlock();
return hr;
@@ -1026,16 +1029,44 @@ static HRESULT WINAPI d3d_device1_NextViewport(IDirect3DDevice *iface,
* x2 and y2 are ignored.
*
* Returns:
- * D3D_OK because it's a stub
+ * D3D_OK on success
+ * DDERR_INVALIDPARAMS if any of the parameters == NULL
*
*****************************************************************************/
static HRESULT WINAPI d3d_device1_Pick(IDirect3DDevice *iface, IDirect3DExecuteBuffer *buffer,
IDirect3DViewport *viewport, DWORD flags, D3DRECT *rect)
{
- FIXME("iface %p, buffer %p, viewport %p, flags %#x, rect %s stub!\n",
+ struct d3d_device *device = impl_from_IDirect3DDevice(iface);
+ struct d3d_execute_buffer *buffer_impl = unsafe_impl_from_IDirect3DExecuteBuffer(buffer);
+ struct d3d_viewport *viewport_impl = unsafe_impl_from_IDirect3DViewport(viewport);
+ HRESULT hr;
+
+ TRACE("iface %p, buffer %p, viewport %p, flags %#x, rect %s.\n",
iface, buffer, viewport, flags, wine_dbgstr_rect((RECT *)rect));
- return D3D_OK;
+ /* Sanity checks */
+ if (!buffer)
+ {
+ WARN("NULL buffer, returning DDERR_INVALIDPARAMS\n");
+ return DDERR_INVALIDPARAMS;
+ }
+
+ if (!viewport)
+ {
+ WARN("NULL viewport, returning DDERR_INVALIDPARAMS\n");
+ return DDERR_INVALIDPARAMS;
+ }
+
+ if (FAILED(hr = IDirect3DDevice3_SetCurrentViewport
+ (&device->IDirect3DDevice3_iface, &viewport_impl->IDirect3DViewport3_iface)))
+ return hr;
+
+ /* Execute the pick */
+ wined3d_mutex_lock();
+ hr = d3d_execute_buffer_execute(buffer_impl, device, rect);
+ wined3d_mutex_unlock();
+
+ return hr;
}
/*****************************************************************************
@@ -1051,13 +1082,38 @@ static HRESULT WINAPI d3d_device1_Pick(IDirect3DDevice *iface, IDirect3DExecuteB
* D3DPickRec: Address to store the resulting D3DPICKRECORD array.
*
* Returns:
- * D3D_OK, because it's a stub
+ * D3D_OK always
*
*****************************************************************************/
static HRESULT WINAPI d3d_device1_GetPickRecords(IDirect3DDevice *iface,
DWORD *count, D3DPICKRECORD *records)
{
- FIXME("iface %p, count %p, records %p stub!\n", iface, count, records);
+ struct d3d_device *device;
+
+ TRACE("iface %p, count %p, records %p.\n", iface, count, records);
+
+ /* Windows doesn't check if count is non-NULL */
+
+ wined3d_mutex_lock();
+
+ device = impl_from_IDirect3DDevice(iface);
+
+ /* Set count to the number of pick records we have */
+ *count = device->pick_record_count;
+
+ /* It is correct usage according to documentation to call this function with records == NULL
+ to retrieve _just_ the record count, which the caller can then use to allocate an
+ appropriately sized array, then call this function again to fill that array with data. */
+ if (records && count)
+ {
+ /* If we have a destination array and records to copy, copy them now */
+ memcpy(records, device->pick_records, sizeof(*device->pick_records) * device->pick_record_count);
+
+ /* We're now done with the old pick records */
+ device->pick_record_count = 0;
+ }
+
+ wined3d_mutex_unlock();
return D3D_OK;
}
diff --git a/dlls/ddraw/executebuffer.c b/dlls/ddraw/executebuffer.c
index e0a9445bf71..f5116573e46 100644
--- a/dlls/ddraw/executebuffer.c
+++ b/dlls/ddraw/executebuffer.c
@@ -45,15 +45,106 @@ static void _dump_D3DEXECUTEBUFFERDESC(const D3DEXECUTEBUFFERDESC *lpDesc) {
TRACE("lpData : %p\n", lpDesc->lpData);
}
-HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d_device *device)
+#define TRIANGLE_SIZE 3
+/*****************************************************************************
+ * d3d_execute_buffer_pick_test
+ *
+ * Determines whether a "point" is inside a "triangle". Mainly used when
+ * executing a "pick" from an execute buffer to determine whether a pixel
+ * coordinate (often a mouse coordinate) is inside a triangle (and
+ * therefore clicking or hovering over a 3D object in the scene). This
+ * function uses triangle rasterization algorithms to determine if the
+ * pixel falls inside (using the top-left rule, in accordance with
+ * documentation).
+ *
+ * Params:
+ * x: The X coordinate of the point to verify.
+ * y: The Y coordinate of the point to verify.
+ * verts: An array of vertices describing the screen coordinates of the
+ * triangle. This function expects 3 elements in this array.
+ *
+ * Returns:
+ * TRUE if the pixel coordinate is inside this triangle
+ * FALSE if not
+ *
+ *****************************************************************************/
+static BOOL d3d_execute_buffer_pick_test(LONG x, LONG y, D3DTLVERTEX* verts)
+{
+ UINT i;
+
+ for (i = 0; i < TRIANGLE_SIZE; i++)
+ {
+ D3DTLVERTEX* v1 = &verts[(i) % TRIANGLE_SIZE];
+ D3DTLVERTEX* v2 = &verts[(i + 1) % TRIANGLE_SIZE];
+ D3DVALUE bias = 0.0f;
+
+ /* Edge function - determines whether pixel is inside triangle */
+ D3DVALUE w = (v2->u1.sx - v1->u1.sx) * (y - v1->u2.sy) - (v2->u2.sy - v1->u2.sy) * (x - v1->u1.sx);
+
+ /* Force top-left rule */
+ if ((v1->u2.sy == v2->u2.sy && v1->u1.sx > v2->u1.sx) || (v1->u2.sy < v2->u2.sy))
+ bias = 1.0f;
+
+ if (w < bias)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************
+ * d3d_execute_buffer_z_value_at_coords
+ *
+ * Returns the Z point of a triangle given an X, Y coordinate somewhere inside
+ * the triangle. Used as the `dvZ` parameter of D3DPICKRECORD.
+ *
+ * Params:
+ * x: The X coordinate of the point to verify.
+ * y: The Y coordinate of the point to verify.
+ * verts: An array of vertices describing the screen coordinates of the
+ * triangle. This function expects 3 elements in this array.
+ *
+ * Returns:
+ * A floating-point Z value that can be used directly as the dvZ member of a
+ * D3DPICKRECORD.
+ *
+ *****************************************************************************/
+static D3DVALUE d3d_execute_buffer_z_value_at_coords(LONG x, LONG y, D3DTLVERTEX* verts)
+{
+ UINT i;
+
+ D3DVALUE z1 = 0;
+ D3DVALUE z2 = 0;
+
+ for (i = 0; i < TRIANGLE_SIZE; i++)
+ {
+ D3DTLVERTEX* v1 = &verts[i];
+ D3DTLVERTEX* v2 = &verts[(i + 1) % TRIANGLE_SIZE];
+ D3DTLVERTEX* v3 = &verts[(i + 2) % TRIANGLE_SIZE];
+
+ z1 += v3->u3.sz * (x - v1->u1.sx) * (y - v2->u2.sy) - v2->u3.sz * (x - v1->u1.sx) * (y - v3->u2.sy);
+ z2 += (x - v1->u1.sx) * (y - v2->u2.sy) - (x - v1->u1.sx) * (y - v3->u2.sy);
+ }
+
+ return z1 / z2;
+}
+
+HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d_device *device,
+ D3DRECT* pick_rect)
{
DWORD is = buffer->data.dwInstructionOffset;
char *instr = (char *)buffer->desc.lpData + is;
unsigned int i, primitive_size;
- struct wined3d_map_desc map_desc;
+ struct wined3d_map_desc map_desc, vert_map_desc;
struct wined3d_box box = {0};
HRESULT hr;
+ /* Variables used for picking */
+ const unsigned int vertex_size = get_flexible_vertex_size(D3DFVF_TLVERTEX);
+ D3DTLVERTEX verts[TRIANGLE_SIZE];
+
+ device->pick_record_count = 0;
+
TRACE("ExecuteData :\n");
if (TRACE_ON(ddraw))
_dump_executedata(&(buffer->data));
@@ -69,6 +160,26 @@ HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d
instr += sizeof(*current);
primitive_size = 0;
+ if (pick_rect != NULL)
+ {
+ switch (current->bOpcode)
+ {
+ /* None of these opcodes seem to be necessary for picking */
+ case D3DOP_POINT:
+ case D3DOP_LINE:
+ case D3DOP_STATETRANSFORM:
+ case D3DOP_STATELIGHT:
+ case D3DOP_STATERENDER:
+ case D3DOP_TEXTURELOAD:
+ case D3DOP_SPAN:
+ FIXME("ignoring opcode %d for picking\n", current->bOpcode);
+ instr += count * size;
+ continue;
+ default:
+ break;
+ }
+ }
+
switch (current->bOpcode)
{
case D3DOP_POINT:
@@ -173,6 +284,66 @@ HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d
{
case 3:
indices[(i * primitive_size) + 2] = ci->u3.v3;
+
+ if (pick_rect != NULL) {
+ UINT j;
+
+ /* Get D3DTLVERTEX objects for each triangle vertex */
+ for (j = 0; j < TRIANGLE_SIZE; j++) {
+
+ /* Get index of vertex from D3DTRIANGLE struct */
+ switch (j) {
+ case 0: box.left = vertex_size * ci->u1.v1; break;
+ case 1: box.left = vertex_size * ci->u2.v2; break;
+ case 2: box.left = vertex_size * ci->u3.v3; break;
+ }
+
+ box.right = box.left + vertex_size;
+ if (FAILED(hr = wined3d_resource_map(wined3d_buffer_get_resource(buffer->dst_vertex_buffer),
+ 0, &vert_map_desc, &box, WINED3D_MAP_WRITE))) {
+ return hr;
+ } else {
+ /* Copy vert data into stack array */
+ verts[j] = *((D3DTLVERTEX*)vert_map_desc.data);
+
+ wined3d_resource_unmap(wined3d_buffer_get_resource(buffer->dst_vertex_buffer), 0);
+ }
+ }
+
+ /* Use vertices acquired above to test for clicking */
+ if (d3d_execute_buffer_pick_test(pick_rect->u1.x1, pick_rect->u2.y1, verts))
+ {
+ D3DPICKRECORD* record;
+
+ device->pick_record_count++;
+
+ /* Grow the array if necessary */
+ if (device->pick_record_count > device->pick_record_size)
+ {
+ if (device->pick_record_size == 0) device->pick_record_size = 1;
+ device->pick_record_size *= 2;
+ device->pick_records = heap_realloc(device->pick_records,
+ sizeof(*device->pick_records) * device->pick_record_size);
+ }
+
+ /* Fill record parameters */
+ record = &device->pick_records[device->pick_record_count - 1];
+
+ record->bOpcode = current->bOpcode;
+ record->bPad = 0;
+
+ /* Write current instruction offset into file */
+ record->dwOffset = (DWORD)instr - (DWORD)buffer->desc.lpData - is;
+
+ /* Formula for returning the Z value at this X/Y */
+ record->dvZ = d3d_execute_buffer_z_value_at_coords(pick_rect->u1.x1, pick_rect->u2.y1, verts);
+
+ /* We have a successful pick so we can skip the rest of the triangles */
+ instr += size * (count - i - 1);
+ count = i;
+ }
+ }
+
/* Drop through. */
case 2:
indices[(i * primitive_size) + 1] = ci->u2.v2;
@@ -183,13 +354,17 @@ HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d
wined3d_resource_unmap(wined3d_buffer_get_resource(buffer->index_buffer), 0);
- wined3d_stateblock_set_stream_source(device->state, 0,
- buffer->dst_vertex_buffer, 0, sizeof(D3DTLVERTEX));
- wined3d_stateblock_set_vertex_declaration(device->state,
- ddraw_find_decl(device->ddraw, D3DFVF_TLVERTEX));
- wined3d_stateblock_set_index_buffer(device->state, buffer->index_buffer, WINED3DFMT_R16_UINT);
- wined3d_device_apply_stateblock(device->wined3d_device, device->state);
- wined3d_device_draw_indexed_primitive(device->wined3d_device, index_pos, index_count);
+ /* Skip drawing if we're picking */
+ if (pick_rect == NULL)
+ {
+ wined3d_stateblock_set_stream_source(device->state, 0,
+ buffer->dst_vertex_buffer, 0, sizeof(D3DTLVERTEX));
+ wined3d_stateblock_set_vertex_declaration(device->state,
+ ddraw_find_decl(device->ddraw, D3DFVF_TLVERTEX));
+ wined3d_stateblock_set_index_buffer(device->state, buffer->index_buffer, WINED3DFMT_R16_UINT);
+ wined3d_device_apply_stateblock(device->wined3d_device, device->state);
+ wined3d_device_draw_indexed_primitive(device->wined3d_device, index_pos, index_count);
+ }
buffer->index_pos = index_pos + index_count;
break;
@@ -423,6 +598,7 @@ HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d
end_of_buffer:
return D3D_OK;
}
+#undef TRIANGLE_SIZE
static inline struct d3d_execute_buffer *impl_from_IDirect3DExecuteBuffer(IDirect3DExecuteBuffer *iface)
{
diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c
index 2fac4d4b0ba..d12433c6315 100644
--- a/dlls/ddraw/tests/ddraw1.c
+++ b/dlls/ddraw/tests/ddraw1.c
@@ -13880,6 +13880,136 @@ static void test_get_display_mode(void)
IDirectDraw_Release(ddraw);
}
+static void test_pick(void)
+{
+ static D3DTLVERTEX tquad[] =
+ {
+ {{320.0f}, {480.0f}, { 1.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}},
+ {{ 0.0f}, {480.0f}, {-0.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}},
+ {{320.0f}, { 0.0f}, { 1.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}},
+ {{ 0.0f}, { 0.0f}, {-0.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}},
+ };
+ IDirect3DExecuteBuffer *execute_buffer;
+ D3DEXECUTEBUFFERDESC exec_desc;
+ IDirect3DViewport *viewport;
+ IDirect3DDevice *device;
+ IDirectDraw *ddraw;
+ UINT inst_length;
+ HWND window;
+ HRESULT hr;
+ void *ptr;
+ DWORD rec_count;
+ D3DRECT pick_rect;
+ UINT screen_width = 640;
+ UINT screen_height = 480;
+ UINT hits = 0;
+ UINT nohits = 0;
+ int i, j;
+
+ window = create_window();
+ ddraw = create_ddraw();
+ ok(!!ddraw, "Failed to create a ddraw object.\n");
+ if (!(device = create_device(ddraw, window, DDSCL_NORMAL)))
+ {
+ skip("Failed to create a 3D device, skipping test.\n");
+ IDirectDraw_Release(ddraw);
+ DestroyWindow(window);
+ return;
+ }
+
+ viewport = create_viewport(device, 0, 0, screen_width, screen_height);
+
+ memset(&exec_desc, 0, sizeof(exec_desc));
+ exec_desc.dwSize = sizeof(exec_desc);
+ exec_desc.dwFlags = D3DDEB_BUFSIZE | D3DDEB_CAPS;
+ exec_desc.dwBufferSize = 1024;
+ exec_desc.dwCaps = D3DDEBCAPS_SYSTEMMEMORY;
+
+ hr = IDirect3DDevice_CreateExecuteBuffer(device, &exec_desc, &execute_buffer, NULL);
+ ok(SUCCEEDED(hr), "Failed to create execute buffer, hr %#x.\n", hr);
+ hr = IDirect3DExecuteBuffer_Lock(execute_buffer, &exec_desc);
+ ok(SUCCEEDED(hr), "Failed to lock execute buffer, hr %#x.\n", hr);
+ memcpy(exec_desc.lpData, tquad, sizeof(tquad));
+ ptr = ((BYTE *)exec_desc.lpData) + sizeof(tquad);
+ emit_process_vertices(&ptr, D3DPROCESSVERTICES_COPY, 0, 4);
+ emit_tquad(&ptr, 0);
+ emit_end(&ptr);
+ inst_length = (BYTE *)ptr - (BYTE *)exec_desc.lpData;
+ inst_length -= sizeof(tquad);
+ hr = IDirect3DExecuteBuffer_Unlock(execute_buffer);
+ ok(SUCCEEDED(hr), "Failed to unlock execute buffer, hr %#x.\n", hr);
+
+ set_execute_data(execute_buffer, 4, sizeof(tquad), inst_length);
+
+ /* Perform a number of picks, we should have a specific amount by the end */
+ for (i = 0; i < screen_width; i += 80)
+ {
+ for (j = 0; j < screen_height; j += 60)
+ {
+ pick_rect.x1 = i;
+ pick_rect.y1 = j;
+
+ hr = IDirect3DDevice_Pick(device, execute_buffer, viewport, 0, &pick_rect);
+ ok(SUCCEEDED(hr), "Failed to perform pick, hr %#x.\n", hr);
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, NULL);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ if (rec_count > 0)
+ hits++;
+ else
+ nohits++;
+ }
+ }
+
+ /*
+ We should have gotten precisely equal numbers of hits and no hits since our quad
+ covers exactly half the screen
+ */
+ ok(hits == nohits, "Got a non-equal amount of pick successes/failures: %i vs %i.\n", hits, nohits);
+
+ /* Try some specific pixel picks */
+ pick_rect.x1 = 480;
+ pick_rect.y1 = 360;
+ hr = IDirect3DDevice_Pick(device, execute_buffer, viewport, 0, &pick_rect);
+ ok(SUCCEEDED(hr), "Failed to perform pick, hr %#x.\n", hr);
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, NULL);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ ok(rec_count == 0, "Got incorrect number of pick records (expected 0): %i.\n", rec_count);
+
+ pick_rect.x1 = 240;
+ pick_rect.y1 = 120;
+ hr = IDirect3DDevice_Pick(device, execute_buffer, viewport, 0, &pick_rect);
+ ok(SUCCEEDED(hr), "Failed to perform pick, hr %#x.\n", hr);
+ rec_count = 0;
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, NULL);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ ok(rec_count == 1, "Got incorrect number of pick records (expected 1): %i.\n", rec_count);
+
+ if (rec_count == 1)
+ {
+ D3DPICKRECORD record;
+
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, &record);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ ok(rec_count == 1, "Got incorrect number of pick records (expected 1): %i.\n", rec_count);
+
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, &record);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ ok(rec_count == 0, "Got incorrect number of pick records (expected 0): %i.\n", rec_count);
+
+ /* Tests D3DPICKRECORD for correct information */
+ ok(record.bOpcode == 3, "Got incorrect bOpcode: %i.\n", record.bOpcode);
+ ok(record.bPad == 0, "Got incorrect bPad: %i.\n", record.bPad);
+ ok(record.dwOffset == 24, "Got incorrect dwOffset: %i.\n", record.dwOffset);
+ ok(compare_float(record.dvZ, 1.0, 0.1), "Got incorrect dvZ: %f.\n", record.dvZ);
+ }
+
+ destroy_viewport(device, viewport);
+ IDirect3DExecuteBuffer_Release(execute_buffer);
+ IDirect3DDevice_Release(device);
+ IDirectDraw_Release(ddraw);
+ DestroyWindow(window);
+}
+
START_TEST(ddraw1)
{
DDDEVICEIDENTIFIER identifier;
@@ -13996,4 +14126,5 @@ START_TEST(ddraw1)
test_vtbl_protection();
test_window_position();
test_get_display_mode();
+ test_pick();
}
--
2.28.0
2
1
From: Matthew Wong <itsmattkc(a)gmail.com>
Implement functions used by some games (notably LEGO Island) for
determining which 3D object in a scene was clicked by the mouse cursor.
Fighting Steel also uses this function for mouse over. Previous stubs
would cause LEGO Island to crash upon any click and Fighting Steel
to crash on game start. A patch posted years ago on the bug thread
provided the minimum functionality to prevent crashes, but still
rendered large portions of the game inaccessible without them
implemented correctly.
Picking has been implemented by adding a "pick mode" in
d3d_execute_buffer_execute() which skips any drawing functions
leaving just the vertex processing. Adds click tests for each triangle
when in pick mode for creating an array of D3DPICKRECORDs.
Stress testing reveals this patch's Pick() implementation may have
slight inaccuracies to the original function; occasionally pixels right
on triangle edges result in successful picks when they don't with the
original function (and vice versa). It may be some sort of floating
point rounding error or other algorithm difference that would be
difficult to determine without seeing the original code. In practice, I
believe this inaccuracy is so negligible that it won't produce any
undesirable results for the user.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=10729
Signed-off-by: Matthew Wong <itsmattkc(a)gmail.com>
Signed-off-by: Myah Caron <qsniyg(a)protonmail.com>
---
Matthew's original patch is available here: https://www.winehq.org/pipermail/wine-devel/2019-August/149695.html.
This version addresses the corrections Stefan pointed out here: https://www.winehq.org/pipermail/wine-devel/2019-November/154023.html.
I've removed the "Add a" paragraphs from the commit message, primarily because the first one is now inaccurate due to the corrections.
The only comment I have (purposefully) not addressed is the last one referring to the corner case. This is because I know next to nothing
about ddraw, and I don't have a testing machine on-hand either (only a VM). This also means that the new tests I have added are entirely
untested on a native machine, so I'll have to rely on Marvin.
Regarding this comment:
>The other question - to be answered by extending the test - is which
>count is used here. Should you copy all pick records in the device, or
>*count pick records, or whichever is smaller, or return an error if
>*count < device->pick_record_count.
I have added rec_count = 0 for the following to explicitly test for this:
rec_count = 0;
hr = IDirect3DDevice_GetPickRecords(device, &rec_count, NULL);
However, since rec_count should have already been set to 0 if the previous GetPickRecords test passed, I believe this means that the
inputted count variable is ignored by GetPickRecords.
dlls/ddraw/ddraw_private.h | 7 +-
dlls/ddraw/device.c | 72 ++++++++++++--
dlls/ddraw/executebuffer.c | 194 +++++++++++++++++++++++++++++++++++--
dlls/ddraw/tests/ddraw1.c | 138 ++++++++++++++++++++++++++
4 files changed, 395 insertions(+), 16 deletions(-)
diff --git a/dlls/ddraw/ddraw_private.h b/dlls/ddraw/ddraw_private.h
index 776f61e1024..9d32aa61b1a 100644
--- a/dlls/ddraw/ddraw_private.h
+++ b/dlls/ddraw/ddraw_private.h
@@ -335,6 +335,11 @@ struct d3d_device
struct d3d_viewport *current_viewport;
D3DVIEWPORT7 active_viewport;
+ /* Pick data */
+ D3DPICKRECORD *pick_records;
+ DWORD pick_record_count;
+ DWORD pick_record_size;
+
/* Required to keep track which of two available texture blending modes in d3ddevice3 is used */
BOOL legacyTextureBlending;
D3DTEXTUREBLEND texture_map_blend;
@@ -568,7 +573,7 @@ struct d3d_execute_buffer *unsafe_impl_from_IDirect3DExecuteBuffer(IDirect3DExec
/* The execute function */
HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *execute_buffer,
- struct d3d_device *device) DECLSPEC_HIDDEN;
+ struct d3d_device *device, D3DRECT *pick_rect) DECLSPEC_HIDDEN;
/*****************************************************************************
* IDirect3DVertexBuffer
diff --git a/dlls/ddraw/device.c b/dlls/ddraw/device.c
index 3f1fe08c11e..4e007695733 100644
--- a/dlls/ddraw/device.c
+++ b/dlls/ddraw/device.c
@@ -350,6 +350,9 @@ static ULONG WINAPI d3d_device_inner_Release(IUnknown *iface)
IDirect3DDevice3_DeleteViewport(&This->IDirect3DDevice3_iface, &vp->IDirect3DViewport3_iface);
}
+ if (This->pick_record_size > 0)
+ heap_free(This->pick_records);
+
TRACE("Releasing render target %p.\n", This->rt_iface);
rt_iface = This->rt_iface;
This->rt_iface = NULL;
@@ -759,7 +762,7 @@ static HRESULT WINAPI d3d_device1_Execute(IDirect3DDevice *iface,
/* Execute... */
wined3d_mutex_lock();
- hr = d3d_execute_buffer_execute(buffer, device);
+ hr = d3d_execute_buffer_execute(buffer, device, NULL);
wined3d_mutex_unlock();
return hr;
@@ -1026,16 +1029,44 @@ static HRESULT WINAPI d3d_device1_NextViewport(IDirect3DDevice *iface,
* x2 and y2 are ignored.
*
* Returns:
- * D3D_OK because it's a stub
+ * D3D_OK on success
+ * DDERR_INVALIDPARAMS if any of the parameters == NULL
*
*****************************************************************************/
static HRESULT WINAPI d3d_device1_Pick(IDirect3DDevice *iface, IDirect3DExecuteBuffer *buffer,
IDirect3DViewport *viewport, DWORD flags, D3DRECT *rect)
{
- FIXME("iface %p, buffer %p, viewport %p, flags %#x, rect %s stub!\n",
+ struct d3d_device *device = impl_from_IDirect3DDevice(iface);
+ struct d3d_execute_buffer *buffer_impl = unsafe_impl_from_IDirect3DExecuteBuffer(buffer);
+ struct d3d_viewport *viewport_impl = unsafe_impl_from_IDirect3DViewport(viewport);
+ HRESULT hr;
+
+ TRACE("iface %p, buffer %p, viewport %p, flags %#x, rect %s.\n",
iface, buffer, viewport, flags, wine_dbgstr_rect((RECT *)rect));
- return D3D_OK;
+ /* Sanity checks */
+ if (!buffer)
+ {
+ WARN("NULL buffer, returning DDERR_INVALIDPARAMS\n");
+ return DDERR_INVALIDPARAMS;
+ }
+
+ if (!viewport)
+ {
+ WARN("NULL viewport, returning DDERR_INVALIDPARAMS\n");
+ return DDERR_INVALIDPARAMS;
+ }
+
+ if (FAILED(hr = IDirect3DDevice3_SetCurrentViewport
+ (&device->IDirect3DDevice3_iface, &viewport_impl->IDirect3DViewport3_iface)))
+ return hr;
+
+ /* Execute the pick */
+ wined3d_mutex_lock();
+ hr = d3d_execute_buffer_execute(buffer_impl, device, rect);
+ wined3d_mutex_unlock();
+
+ return hr;
}
/*****************************************************************************
@@ -1051,13 +1082,42 @@ static HRESULT WINAPI d3d_device1_Pick(IDirect3DDevice *iface, IDirect3DExecuteB
* D3DPickRec: Address to store the resulting D3DPICKRECORD array.
*
* Returns:
- * D3D_OK, because it's a stub
+ * D3D_OK always
*
*****************************************************************************/
static HRESULT WINAPI d3d_device1_GetPickRecords(IDirect3DDevice *iface,
DWORD *count, D3DPICKRECORD *records)
{
- FIXME("iface %p, count %p, records %p stub!\n", iface, count, records);
+ struct d3d_device *device;
+
+ TRACE("iface %p, count %p, records %p.\n", iface, count, records);
+
+ if (!count)
+ {
+ WARN("NULL count, returning DDERR_INVALIDPARAMS\n");
+ return DDERR_INVALIDPARAMS;
+ }
+
+ wined3d_mutex_lock();
+
+ device = impl_from_IDirect3DDevice(iface);
+
+ /* Set count to the number of pick records we have */
+ *count = device->pick_record_count;
+
+ /* It is correct usage according to documentation to call this function with records == NULL
+ to retrieve _just_ the record count, which the caller can then use to allocate an
+ appropriately sized array, then call this function again to fill that array with data. */
+ if (records && count)
+ {
+ /* If we have a destination array and records to copy, copy them now */
+ memcpy(records, device->pick_records, sizeof(*device->pick_records) * device->pick_record_count);
+
+ /* We're now done with the old pick records */
+ device->pick_record_count = 0;
+ }
+
+ wined3d_mutex_unlock();
return D3D_OK;
}
diff --git a/dlls/ddraw/executebuffer.c b/dlls/ddraw/executebuffer.c
index e0a9445bf71..f5116573e46 100644
--- a/dlls/ddraw/executebuffer.c
+++ b/dlls/ddraw/executebuffer.c
@@ -45,15 +45,106 @@ static void _dump_D3DEXECUTEBUFFERDESC(const D3DEXECUTEBUFFERDESC *lpDesc) {
TRACE("lpData : %p\n", lpDesc->lpData);
}
-HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d_device *device)
+#define TRIANGLE_SIZE 3
+/*****************************************************************************
+ * d3d_execute_buffer_pick_test
+ *
+ * Determines whether a "point" is inside a "triangle". Mainly used when
+ * executing a "pick" from an execute buffer to determine whether a pixel
+ * coordinate (often a mouse coordinate) is inside a triangle (and
+ * therefore clicking or hovering over a 3D object in the scene). This
+ * function uses triangle rasterization algorithms to determine if the
+ * pixel falls inside (using the top-left rule, in accordance with
+ * documentation).
+ *
+ * Params:
+ * x: The X coordinate of the point to verify.
+ * y: The Y coordinate of the point to verify.
+ * verts: An array of vertices describing the screen coordinates of the
+ * triangle. This function expects 3 elements in this array.
+ *
+ * Returns:
+ * TRUE if the pixel coordinate is inside this triangle
+ * FALSE if not
+ *
+ *****************************************************************************/
+static BOOL d3d_execute_buffer_pick_test(LONG x, LONG y, D3DTLVERTEX* verts)
+{
+ UINT i;
+
+ for (i = 0; i < TRIANGLE_SIZE; i++)
+ {
+ D3DTLVERTEX* v1 = &verts[(i) % TRIANGLE_SIZE];
+ D3DTLVERTEX* v2 = &verts[(i + 1) % TRIANGLE_SIZE];
+ D3DVALUE bias = 0.0f;
+
+ /* Edge function - determines whether pixel is inside triangle */
+ D3DVALUE w = (v2->u1.sx - v1->u1.sx) * (y - v1->u2.sy) - (v2->u2.sy - v1->u2.sy) * (x - v1->u1.sx);
+
+ /* Force top-left rule */
+ if ((v1->u2.sy == v2->u2.sy && v1->u1.sx > v2->u1.sx) || (v1->u2.sy < v2->u2.sy))
+ bias = 1.0f;
+
+ if (w < bias)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************
+ * d3d_execute_buffer_z_value_at_coords
+ *
+ * Returns the Z point of a triangle given an X, Y coordinate somewhere inside
+ * the triangle. Used as the `dvZ` parameter of D3DPICKRECORD.
+ *
+ * Params:
+ * x: The X coordinate of the point to verify.
+ * y: The Y coordinate of the point to verify.
+ * verts: An array of vertices describing the screen coordinates of the
+ * triangle. This function expects 3 elements in this array.
+ *
+ * Returns:
+ * A floating-point Z value that can be used directly as the dvZ member of a
+ * D3DPICKRECORD.
+ *
+ *****************************************************************************/
+static D3DVALUE d3d_execute_buffer_z_value_at_coords(LONG x, LONG y, D3DTLVERTEX* verts)
+{
+ UINT i;
+
+ D3DVALUE z1 = 0;
+ D3DVALUE z2 = 0;
+
+ for (i = 0; i < TRIANGLE_SIZE; i++)
+ {
+ D3DTLVERTEX* v1 = &verts[i];
+ D3DTLVERTEX* v2 = &verts[(i + 1) % TRIANGLE_SIZE];
+ D3DTLVERTEX* v3 = &verts[(i + 2) % TRIANGLE_SIZE];
+
+ z1 += v3->u3.sz * (x - v1->u1.sx) * (y - v2->u2.sy) - v2->u3.sz * (x - v1->u1.sx) * (y - v3->u2.sy);
+ z2 += (x - v1->u1.sx) * (y - v2->u2.sy) - (x - v1->u1.sx) * (y - v3->u2.sy);
+ }
+
+ return z1 / z2;
+}
+
+HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d_device *device,
+ D3DRECT* pick_rect)
{
DWORD is = buffer->data.dwInstructionOffset;
char *instr = (char *)buffer->desc.lpData + is;
unsigned int i, primitive_size;
- struct wined3d_map_desc map_desc;
+ struct wined3d_map_desc map_desc, vert_map_desc;
struct wined3d_box box = {0};
HRESULT hr;
+ /* Variables used for picking */
+ const unsigned int vertex_size = get_flexible_vertex_size(D3DFVF_TLVERTEX);
+ D3DTLVERTEX verts[TRIANGLE_SIZE];
+
+ device->pick_record_count = 0;
+
TRACE("ExecuteData :\n");
if (TRACE_ON(ddraw))
_dump_executedata(&(buffer->data));
@@ -69,6 +160,26 @@ HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d
instr += sizeof(*current);
primitive_size = 0;
+ if (pick_rect != NULL)
+ {
+ switch (current->bOpcode)
+ {
+ /* None of these opcodes seem to be necessary for picking */
+ case D3DOP_POINT:
+ case D3DOP_LINE:
+ case D3DOP_STATETRANSFORM:
+ case D3DOP_STATELIGHT:
+ case D3DOP_STATERENDER:
+ case D3DOP_TEXTURELOAD:
+ case D3DOP_SPAN:
+ FIXME("ignoring opcode %d for picking\n", current->bOpcode);
+ instr += count * size;
+ continue;
+ default:
+ break;
+ }
+ }
+
switch (current->bOpcode)
{
case D3DOP_POINT:
@@ -173,6 +284,66 @@ HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d
{
case 3:
indices[(i * primitive_size) + 2] = ci->u3.v3;
+
+ if (pick_rect != NULL) {
+ UINT j;
+
+ /* Get D3DTLVERTEX objects for each triangle vertex */
+ for (j = 0; j < TRIANGLE_SIZE; j++) {
+
+ /* Get index of vertex from D3DTRIANGLE struct */
+ switch (j) {
+ case 0: box.left = vertex_size * ci->u1.v1; break;
+ case 1: box.left = vertex_size * ci->u2.v2; break;
+ case 2: box.left = vertex_size * ci->u3.v3; break;
+ }
+
+ box.right = box.left + vertex_size;
+ if (FAILED(hr = wined3d_resource_map(wined3d_buffer_get_resource(buffer->dst_vertex_buffer),
+ 0, &vert_map_desc, &box, WINED3D_MAP_WRITE))) {
+ return hr;
+ } else {
+ /* Copy vert data into stack array */
+ verts[j] = *((D3DTLVERTEX*)vert_map_desc.data);
+
+ wined3d_resource_unmap(wined3d_buffer_get_resource(buffer->dst_vertex_buffer), 0);
+ }
+ }
+
+ /* Use vertices acquired above to test for clicking */
+ if (d3d_execute_buffer_pick_test(pick_rect->u1.x1, pick_rect->u2.y1, verts))
+ {
+ D3DPICKRECORD* record;
+
+ device->pick_record_count++;
+
+ /* Grow the array if necessary */
+ if (device->pick_record_count > device->pick_record_size)
+ {
+ if (device->pick_record_size == 0) device->pick_record_size = 1;
+ device->pick_record_size *= 2;
+ device->pick_records = heap_realloc(device->pick_records,
+ sizeof(*device->pick_records) * device->pick_record_size);
+ }
+
+ /* Fill record parameters */
+ record = &device->pick_records[device->pick_record_count - 1];
+
+ record->bOpcode = current->bOpcode;
+ record->bPad = 0;
+
+ /* Write current instruction offset into file */
+ record->dwOffset = (DWORD)instr - (DWORD)buffer->desc.lpData - is;
+
+ /* Formula for returning the Z value at this X/Y */
+ record->dvZ = d3d_execute_buffer_z_value_at_coords(pick_rect->u1.x1, pick_rect->u2.y1, verts);
+
+ /* We have a successful pick so we can skip the rest of the triangles */
+ instr += size * (count - i - 1);
+ count = i;
+ }
+ }
+
/* Drop through. */
case 2:
indices[(i * primitive_size) + 1] = ci->u2.v2;
@@ -183,13 +354,17 @@ HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d
wined3d_resource_unmap(wined3d_buffer_get_resource(buffer->index_buffer), 0);
- wined3d_stateblock_set_stream_source(device->state, 0,
- buffer->dst_vertex_buffer, 0, sizeof(D3DTLVERTEX));
- wined3d_stateblock_set_vertex_declaration(device->state,
- ddraw_find_decl(device->ddraw, D3DFVF_TLVERTEX));
- wined3d_stateblock_set_index_buffer(device->state, buffer->index_buffer, WINED3DFMT_R16_UINT);
- wined3d_device_apply_stateblock(device->wined3d_device, device->state);
- wined3d_device_draw_indexed_primitive(device->wined3d_device, index_pos, index_count);
+ /* Skip drawing if we're picking */
+ if (pick_rect == NULL)
+ {
+ wined3d_stateblock_set_stream_source(device->state, 0,
+ buffer->dst_vertex_buffer, 0, sizeof(D3DTLVERTEX));
+ wined3d_stateblock_set_vertex_declaration(device->state,
+ ddraw_find_decl(device->ddraw, D3DFVF_TLVERTEX));
+ wined3d_stateblock_set_index_buffer(device->state, buffer->index_buffer, WINED3DFMT_R16_UINT);
+ wined3d_device_apply_stateblock(device->wined3d_device, device->state);
+ wined3d_device_draw_indexed_primitive(device->wined3d_device, index_pos, index_count);
+ }
buffer->index_pos = index_pos + index_count;
break;
@@ -423,6 +598,7 @@ HRESULT d3d_execute_buffer_execute(struct d3d_execute_buffer *buffer, struct d3d
end_of_buffer:
return D3D_OK;
}
+#undef TRIANGLE_SIZE
static inline struct d3d_execute_buffer *impl_from_IDirect3DExecuteBuffer(IDirect3DExecuteBuffer *iface)
{
diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c
index 2fac4d4b0ba..6a598aa6a84 100644
--- a/dlls/ddraw/tests/ddraw1.c
+++ b/dlls/ddraw/tests/ddraw1.c
@@ -13880,6 +13880,143 @@ static void test_get_display_mode(void)
IDirectDraw_Release(ddraw);
}
+static void test_pick(void)
+{
+ static D3DTLVERTEX tquad[] =
+ {
+ {{320.0f}, {480.0f}, { 1.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}},
+ {{ 0.0f}, {480.0f}, {-0.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}},
+ {{320.0f}, { 0.0f}, { 1.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}},
+ {{ 0.0f}, { 0.0f}, {-0.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}},
+ };
+ IDirect3DExecuteBuffer *execute_buffer;
+ D3DEXECUTEBUFFERDESC exec_desc;
+ IDirect3DViewport *viewport;
+ IDirect3DDevice *device;
+ IDirectDraw *ddraw;
+ UINT inst_length;
+ HWND window;
+ HRESULT hr;
+ void *ptr;
+ DWORD rec_count;
+ D3DRECT pick_rect;
+ UINT screen_width = 640;
+ UINT screen_height = 480;
+ UINT hits = 0;
+ UINT nohits = 0;
+ int i, j;
+
+ window = create_window();
+ ddraw = create_ddraw();
+ ok(!!ddraw, "Failed to create a ddraw object.\n");
+ if (!(device = create_device(ddraw, window, DDSCL_NORMAL)))
+ {
+ skip("Failed to create a 3D device, skipping test.\n");
+ IDirectDraw_Release(ddraw);
+ DestroyWindow(window);
+ return;
+ }
+
+ viewport = create_viewport(device, 0, 0, screen_width, screen_height);
+
+ memset(&exec_desc, 0, sizeof(exec_desc));
+ exec_desc.dwSize = sizeof(exec_desc);
+ exec_desc.dwFlags = D3DDEB_BUFSIZE | D3DDEB_CAPS;
+ exec_desc.dwBufferSize = 1024;
+ exec_desc.dwCaps = D3DDEBCAPS_SYSTEMMEMORY;
+
+ hr = IDirect3DDevice_CreateExecuteBuffer(device, &exec_desc, &execute_buffer, NULL);
+ ok(SUCCEEDED(hr), "Failed to create execute buffer, hr %#x.\n", hr);
+ hr = IDirect3DExecuteBuffer_Lock(execute_buffer, &exec_desc);
+ ok(SUCCEEDED(hr), "Failed to lock execute buffer, hr %#x.\n", hr);
+ memcpy(exec_desc.lpData, tquad, sizeof(tquad));
+ ptr = ((BYTE *)exec_desc.lpData) + sizeof(tquad);
+ emit_process_vertices(&ptr, D3DPROCESSVERTICES_COPY, 0, 4);
+ emit_tquad(&ptr, 0);
+ emit_end(&ptr);
+ inst_length = (BYTE *)ptr - (BYTE *)exec_desc.lpData;
+ inst_length -= sizeof(tquad);
+ hr = IDirect3DExecuteBuffer_Unlock(execute_buffer);
+ ok(SUCCEEDED(hr), "Failed to unlock execute buffer, hr %#x.\n", hr);
+
+ set_execute_data(execute_buffer, 4, sizeof(tquad), inst_length);
+
+ hr = IDirect3DDevice_Pick(device, NULL, viewport, 0, &pick_rect);
+ ok(hr == DDERR_INVALIDPARAMS, "got %08x\n", hr);
+ hr = IDirect3DDevice_Pick(device, execute_buffer, NULL, 0, &pick_rect);
+ ok(hr == DDERR_INVALIDPARAMS, "got %08x\n", hr);
+ hr = IDirect3DDevice_GetPickRecords(device, NULL, NULL);
+ ok(hr == DDERR_INVALIDPARAMS, "got %08x\n", hr);
+
+ /* Perform a number of picks, we should have a specific amount by the end */
+ for (i = 0; i < screen_width; i += 80)
+ {
+ for (j = 0; j < screen_height; j += 60)
+ {
+ pick_rect.x1 = i;
+ pick_rect.y1 = j;
+
+ hr = IDirect3DDevice_Pick(device, execute_buffer, viewport, 0, &pick_rect);
+ ok(SUCCEEDED(hr), "Failed to perform pick, hr %#x.\n", hr);
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, NULL);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ if (rec_count > 0)
+ hits++;
+ else
+ nohits++;
+ }
+ }
+
+ /*
+ We should have gotten precisely equal numbers of hits and no hits since our quad
+ covers exactly half the screen
+ */
+ ok(hits == nohits, "Got a non-equal amount of pick successes/failures: %i vs %i.\n", hits, nohits);
+
+ /* Try some specific pixel picks */
+ pick_rect.x1 = 480;
+ pick_rect.y1 = 360;
+ hr = IDirect3DDevice_Pick(device, execute_buffer, viewport, 0, &pick_rect);
+ ok(SUCCEEDED(hr), "Failed to perform pick, hr %#x.\n", hr);
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, NULL);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ ok(rec_count == 0, "Got incorrect number of pick records (expected 0): %i.\n", rec_count);
+
+ pick_rect.x1 = 240;
+ pick_rect.y1 = 120;
+ hr = IDirect3DDevice_Pick(device, execute_buffer, viewport, 0, &pick_rect);
+ ok(SUCCEEDED(hr), "Failed to perform pick, hr %#x.\n", hr);
+ rec_count = 0;
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, NULL);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ ok(rec_count == 1, "Got incorrect number of pick records (expected 1): %i.\n", rec_count);
+
+ if (rec_count == 1)
+ {
+ D3DPICKRECORD record;
+
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, &record);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ ok(rec_count == 1, "Got incorrect number of pick records (expected 1): %i.\n", rec_count);
+
+ hr = IDirect3DDevice_GetPickRecords(device, &rec_count, &record);
+ ok(SUCCEEDED(hr), "Failed to get pick records, hr %#x.\n", hr);
+ ok(rec_count == 0, "Got incorrect number of pick records (expected 0): %i.\n", rec_count);
+
+ /* Tests D3DPICKRECORD for correct information */
+ ok(record.bOpcode == 3, "Got incorrect bOpcode: %i.\n", record.bOpcode);
+ ok(record.bPad == 0, "Got incorrect bPad: %i.\n", record.bPad);
+ ok(record.dwOffset == 24, "Got incorrect dwOffset: %i.\n", record.dwOffset);
+ ok(compare_float(record.dvZ, 1.0, 0.1), "Got incorrect dvZ: %f.\n", record.dvZ);
+ }
+
+ destroy_viewport(device, viewport);
+ IDirect3DExecuteBuffer_Release(execute_buffer);
+ IDirect3DDevice_Release(device);
+ IDirectDraw_Release(ddraw);
+ DestroyWindow(window);
+}
+
START_TEST(ddraw1)
{
DDDEVICEIDENTIFIER identifier;
@@ -13996,4 +14133,5 @@ START_TEST(ddraw1)
test_vtbl_protection();
test_window_position();
test_get_display_mode();
+ test_pick();
}
--
2.28.0
2
2
This is a (partial) resend, per comments that a previous 13-part
series was just too much at once (and it did in fact just age off the
patch queue without any further reply, so zf was obviously correct)
https://www.winehq.org/pipermail/wine-devel/2020-August/171427.html
is the previous full series if anyone *would* like more context of what
what bug(s) this is headed toward. But here's a smaller bite that can
stand alone. Mostly just thickens up the test_dump_typelib
conformance test coverage, fixing one minor widl bug that was found.
Note that most of the changes are just insertions/formatting in
static const type_info info[], which is autogenerated data being printed
or verified by the few lines of code change that *aren't* that.
Kevin Puetz (4):
oleaut32/tests: reformat test_dump_typelib.
oleaut32/tests: Fix expect_wstr_acpval(...,NULL).
widl: all VARDESC fields of TKIND_UNION should have oInst=0.
oleaut32/tests: Cover GetVarDesc in test_dump_typelib.
dlls/oleaut32/tests/typelib.c | 536 ++++++++++++++++++++++++++++++----
tools/widl/write_msft.c | 3 +-
2 files changed, 484 insertions(+), 55 deletions(-)
1
4
Have the winebuild -spec.o include an undefined .globl referencing
symbols we know the winecrt0 entry point will eventually reference,
so ld knows about what it needs before scanning library archives.
Signed-off-by: Kevin Puetz <PuetzKevinA(a)JohnDeere.com>
---
wine.exe.so files set __wine_spec_exe_entry as the entry point.
Therefore the linker is not initially looking for main and will pass
over objects in lib.a archives that might provide it. When it gets to
libwinecrt0.a is reached, it finds __wine_spec_exe_entry and learns
that this wants main, but the resulting rescan will find the winecrt0
definition in terms of WinMain, before rescanning earlier libraries.
So only a direct .o file (which is never passed over) can override
the winecrt0 definition, and libraries like -lgtest_main do not work.
---
tools/winebuild/build.h | 2 ++
tools/winebuild/main.c | 22 ++++++++++++++++++++--
tools/winebuild/spec32.c | 12 ++++++++++++
tools/winegcc/winegcc.c | 4 ++--
4 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h
index 03715af956..a4b9bc97e3 100644
--- a/tools/winebuild/build.h
+++ b/tools/winebuild/build.h
@@ -136,6 +136,7 @@ typedef struct
int subsystem; /* subsystem id */
int subsystem_major; /* subsystem version major number */
int subsystem_minor; /* subsystem version minor number */
+ int unicode_app; /* default to unicode entry point */
ORDDEF *entry_points; /* dll entry points */
ORDDEF **names; /* array of entry point names (points into entry_points) */
ORDDEF **ordinals; /* array of dll ordinals (points into entry_points) */
@@ -299,6 +300,7 @@ extern void output_gnu_stack_note(void);
extern void add_import_dll( const char *name, const char *filename );
extern void add_delayed_import( const char *name );
extern void add_extra_ld_symbol( const char *name );
+extern void add_spec_extra_ld_symbol( const char *name );
extern void read_undef_symbols( DLLSPEC *spec, char **argv );
extern void resolve_imports( DLLSPEC *spec );
extern int is_undefined( const char *name );
diff --git a/tools/winebuild/main.c b/tools/winebuild/main.c
index 0e5e1627dd..68137b0f4c 100644
--- a/tools/winebuild/main.c
+++ b/tools/winebuild/main.c
@@ -396,8 +396,25 @@ static const char *get_default_entry_point( const DLLSPEC *spec )
{
if (spec->characteristics & IMAGE_FILE_DLL) return "DllMain";
if (spec->subsystem == IMAGE_SUBSYSTEM_NATIVE) return "DriverEntry";
- if (spec->type == SPEC_WIN16) return "__wine_spec_exe16_entry";
- return "__wine_spec_exe_entry";
+ if (spec->type == SPEC_WIN16) {
+ add_spec_extra_ld_symbol("WinMain16");
+ return "__wine_spec_exe16_entry";
+ }
+ if (spec->unicode_app) {
+ /* __wine_spec_exe_wentry always calls wmain */
+ add_spec_extra_ld_symbol("wmain");
+ if(spec->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
+ add_spec_extra_ld_symbol("wWinMain");
+ }
+ return "__wine_spec_exe_wentry";
+ } else {
+ /* __wine_spec_exe_entry always calls main */
+ add_spec_extra_ld_symbol("main");
+ if(spec->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
+ add_spec_extra_ld_symbol("WinMain");
+ }
+ return "__wine_spec_exe_entry";
+ }
}
/* parse options from the argv array and remove all the recognized ones */
@@ -448,6 +465,7 @@ static char **parse_options( int argc, char **argv, DLLSPEC *spec )
else if (!strcmp( optarg, "thumb" )) thumb_mode = 1;
else if (!strcmp( optarg, "no-cygwin" )) use_msvcrt = 1;
else if (!strcmp( optarg, "unix" )) unix_lib = 1;
+ else if (!strcmp( optarg, "unicode" )) spec->unicode_app = 1;
else if (!strncmp( optarg, "cpu=", 4 )) cpu_option = xstrdup( optarg + 4 );
else if (!strncmp( optarg, "fpu=", 4 )) fpu_option = xstrdup( optarg + 4 );
else if (!strncmp( optarg, "arch=", 5 )) arch_option = xstrdup( optarg + 5 );
diff --git a/tools/winebuild/spec32.c b/tools/winebuild/spec32.c
index efb136a8e6..02ad498e35 100644
--- a/tools/winebuild/spec32.c
+++ b/tools/winebuild/spec32.c
@@ -50,6 +50,13 @@ int needs_get_pc_thunk = 0;
static const char builtin_signature[32] = "Wine builtin DLL";
static const char fakedll_signature[32] = "Wine placeholder DLL";
+static struct strarray spec_extra_ld_symbols = { 0 }; /* list of extra symbols that ld should resolve */
+
+/* add a symbol to the list of extra symbols that ld must resolve */
+void add_spec_extra_ld_symbol( const char *name )
+{
+ strarray_add( &spec_extra_ld_symbols, name, NULL );
+}
/* check if entry point needs a relay thunk */
static inline int needs_relay( const ORDDEF *odp )
@@ -602,6 +609,7 @@ void output_exports( DLLSPEC *spec )
void output_module( DLLSPEC *spec )
{
int machine = 0;
+ int i;
unsigned int page_size = get_page_size();
const char *data_dirs[16] = { NULL };
@@ -680,6 +688,10 @@ void output_module( DLLSPEC *spec )
output( "\t.long 0\n" ); /* SizeOfCode */
output( "\t.long 0\n" ); /* SizeOfInitializedData */
output( "\t.long 0\n" ); /* SizeOfUninitializedData */
+
+ for (i = 0; i < spec_extra_ld_symbols.count; i++)
+ output( "\t.globl %s\n", asm_name(spec_extra_ld_symbols.str[i]) );
+
/* note: we expand the AddressOfEntryPoint field on 64-bit by overwriting the BaseOfCode field */
output( "\t%s %s\n", /* AddressOfEntryPoint */
get_asm_ptr_keyword(), spec->init_func ? asm_name(spec->init_func) : "0" );
diff --git a/tools/winegcc/winegcc.c b/tools/winegcc/winegcc.c
index bbb44ff614..2cb122613e 100644
--- a/tools/winegcc/winegcc.c
+++ b/tools/winegcc/winegcc.c
@@ -1239,8 +1239,6 @@ static void build(struct options* opts)
entry_point = (is_pe && opts->target_cpu == CPU_x86) ? "DriverEntry(a)8" : "DriverEntry";
else if (opts->use_msvcrt && !opts->shared && !opts->win16_app)
entry_point = opts->unicode_app ? "wmainCRTStartup" : "mainCRTStartup";
- else if (!is_pe && !opts->shared && opts->unicode_app)
- entry_point = "__wine_spec_exe_wentry";
}
else entry_point = opts->entry_point;
@@ -1252,6 +1250,8 @@ static void build(struct options* opts)
spec_o_name = get_temp_file(output_name, ".spec.o");
if (opts->force_pointer_size)
strarray_add(spec_args, strmake("-m%u", 8 * opts->force_pointer_size ));
+ if(opts->unicode_app)
+ strarray_add(spec_args, "-municode");
strarray_add(spec_args, "-D_REENTRANT");
if (opts->pic && !is_pe) strarray_add(spec_args, "-fPIC");
strarray_add(spec_args, opts->shared ? "--dll" : "--exe");
1
0