VirtualBox

source: vbox/trunk/src/libs/dxvk-native-1.9.2a/tests/d3d11/test_d3d11_triangle.cpp@ 99553

最後變更 在這個檔案從99553是 96497,由 vboxsync 提交於 3 年 前

libs/dxvk-native-1.9.2a: export to OSE

  • 屬性 svn:eol-style 設為 native
檔案大小: 17.8 KB
 
1#include <d3dcompiler.h>
2#include <d3d11_1.h>
3#include <dxgi1_3.h>
4
5#include <windows.h>
6#include <windowsx.h>
7
8#include <cstring>
9#include <string>
10#include <sstream>
11
12#include "../test_utils.h"
13
14using namespace dxvk;
15
16struct Vertex {
17 float x, y;
18};
19
20struct VsConstants {
21 float x, y;
22 float w, h;
23};
24
25struct VsConstantsPad {
26 VsConstants data;
27 uint32_t pad[60];
28};
29
30struct PsConstants {
31 float r, g, b, a;
32};
33
34struct DrawOptions {
35 bool mapDiscardOnce;
36 bool sortByTexture;
37 bool drawIndexed;
38};
39
40const std::string g_vertexShaderCode =
41 "cbuffer vs_cb : register(b0) {\n"
42 " float2 v_offset;\n"
43 " float2 v_scale;\n"
44 "};\n"
45 "float4 main(float4 v_pos : IN_POSITION) : SV_POSITION {\n"
46 " float2 coord = 2.0f * (v_pos * v_scale + v_offset) - 1.0f;\n"
47 " return float4(coord, 0.0f, 1.0f);\n"
48 "}\n";
49
50const std::string g_pixelShaderCode =
51 "Texture2D<float4> tex0 : register(t0);"
52 "cbuffer ps_cb : register(b0) {\n"
53 " float4 color;\n"
54 "};\n"
55 "float4 main() : SV_TARGET {\n"
56 " return color * tex0.Load(int3(0, 0, 0));\n"
57 "}\n";
58
59class TriangleApp {
60
61public:
62
63 TriangleApp(HINSTANCE instance, HWND window)
64 : m_window(window) {
65 Com<ID3D11Device> device;
66
67 D3D_FEATURE_LEVEL fl = D3D_FEATURE_LEVEL_11_1;
68
69 HRESULT status = D3D11CreateDevice(
70 nullptr, D3D_DRIVER_TYPE_HARDWARE,
71 nullptr, 0, &fl, 1, D3D11_SDK_VERSION,
72 &device, nullptr, nullptr);
73
74 if (FAILED(status)) {
75 std::cerr << "Failed to create D3D11 device" << std::endl;
76 return;
77 }
78
79 if (FAILED(device->QueryInterface(IID_PPV_ARGS(&m_device)))) {
80 std::cerr << "Failed to query ID3D11DeviceContext1" << std::endl;
81 return;
82 }
83
84 Com<IDXGIDevice> dxgiDevice;
85
86 if (FAILED(m_device->QueryInterface(IID_PPV_ARGS(&dxgiDevice)))) {
87 std::cerr << "Failed to query DXGI device" << std::endl;
88 return;
89 }
90
91 if (FAILED(dxgiDevice->GetAdapter(&m_adapter))) {
92 std::cerr << "Failed to query DXGI adapter" << std::endl;
93 return;
94 }
95
96 if (FAILED(m_adapter->GetParent(IID_PPV_ARGS(&m_factory)))) {
97 std::cerr << "Failed to query DXGI factory" << std::endl;
98 return;
99 }
100
101 m_device->GetImmediateContext1(&m_context);
102
103 DXGI_SWAP_CHAIN_DESC1 swapDesc;
104 swapDesc.Width = m_windowSizeW;
105 swapDesc.Height = m_windowSizeH;
106 swapDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
107 swapDesc.Stereo = FALSE;
108 swapDesc.SampleDesc = { 1, 0 };
109 swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
110 swapDesc.BufferCount = 3;
111 swapDesc.Scaling = DXGI_SCALING_STRETCH;
112 swapDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
113 swapDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
114 swapDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT
115 | DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
116
117 DXGI_SWAP_CHAIN_FULLSCREEN_DESC fsDesc;
118 fsDesc.RefreshRate = { 0, 0 };
119 fsDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
120 fsDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
121 fsDesc.Windowed = TRUE;
122
123 Com<IDXGISwapChain1> swapChain;
124 if (FAILED(m_factory->CreateSwapChainForHwnd(m_device.ptr(), m_window, &swapDesc, &fsDesc, nullptr, &swapChain))) {
125 std::cerr << "Failed to create DXGI swap chain" << std::endl;
126 return;
127 }
128
129 if (FAILED(swapChain->QueryInterface(IID_PPV_ARGS(&m_swapChain)))) {
130 std::cerr << "Failed to query DXGI swap chain interface" << std::endl;
131 return;
132 }
133
134 m_factory->MakeWindowAssociation(m_window, 0);
135
136 Com<ID3DBlob> vertexShaderBlob;
137 Com<ID3DBlob> pixelShaderBlob;
138
139 if (FAILED(D3DCompile(g_vertexShaderCode.data(), g_vertexShaderCode.size(),
140 "Vertex shader", nullptr, nullptr, "main", "vs_5_0", 0, 0, &vertexShaderBlob, nullptr))) {
141 std::cerr << "Failed to compile vertex shader" << std::endl;
142 return;
143 }
144
145 if (FAILED(D3DCompile(g_pixelShaderCode.data(), g_pixelShaderCode.size(),
146 "Pixel shader", nullptr, nullptr, "main", "ps_5_0", 0, 0, &pixelShaderBlob, nullptr))) {
147 std::cerr << "Failed to compile pixel shader" << std::endl;
148 return;
149 }
150
151 if (FAILED(m_device->CreateVertexShader(
152 vertexShaderBlob->GetBufferPointer(),
153 vertexShaderBlob->GetBufferSize(),
154 nullptr, &m_vs))) {
155 std::cerr << "Failed to create vertex shader" << std::endl;
156 return;
157 }
158
159 if (FAILED(m_device->CreatePixelShader(
160 pixelShaderBlob->GetBufferPointer(),
161 pixelShaderBlob->GetBufferSize(),
162 nullptr, &m_ps))) {
163 std::cerr << "Failed to create pixel shader" << std::endl;
164 return;
165 }
166
167 std::array<D3D11_INPUT_ELEMENT_DESC, 1> vertexFormatDesc = {{
168 { "IN_POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
169 }};
170
171 if (FAILED(m_device->CreateInputLayout(
172 vertexFormatDesc.data(),
173 vertexFormatDesc.size(),
174 vertexShaderBlob->GetBufferPointer(),
175 vertexShaderBlob->GetBufferSize(),
176 &m_vertexFormat))) {
177 std::cerr << "Failed to create input layout" << std::endl;
178 return;
179 }
180
181 std::array<Vertex, 6> vertexData = {{
182 Vertex { -0.3f, 0.1f },
183 Vertex { 0.5f, 0.9f },
184 Vertex { 1.3f, 0.1f },
185 Vertex { -0.3f, 0.9f },
186 Vertex { 1.3f, 0.9f },
187 Vertex { 0.5f, 0.1f },
188 }};
189
190 D3D11_BUFFER_DESC vboDesc;
191 vboDesc.ByteWidth = sizeof(vertexData);
192 vboDesc.Usage = D3D11_USAGE_IMMUTABLE;
193 vboDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
194 vboDesc.CPUAccessFlags = 0;
195 vboDesc.MiscFlags = 0;
196 vboDesc.StructureByteStride = 0;
197
198 D3D11_SUBRESOURCE_DATA vboData;
199 vboData.pSysMem = vertexData.data();
200 vboData.SysMemPitch = vboDesc.ByteWidth;
201 vboData.SysMemSlicePitch = vboDesc.ByteWidth;
202
203 if (FAILED(m_device->CreateBuffer(&vboDesc, &vboData, &m_vbo))) {
204 std::cerr << "Failed to create index buffer" << std::endl;
205 return;
206 }
207
208 std::array<uint32_t, 6> indexData = {{ 0, 1, 2, 3, 4, 5 }};
209
210 D3D11_BUFFER_DESC iboDesc;
211 iboDesc.ByteWidth = sizeof(indexData);
212 iboDesc.Usage = D3D11_USAGE_IMMUTABLE;
213 iboDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
214 iboDesc.CPUAccessFlags = 0;
215 iboDesc.MiscFlags = 0;
216 iboDesc.StructureByteStride = 0;
217
218 D3D11_SUBRESOURCE_DATA iboData;
219 iboData.pSysMem = indexData.data();
220 iboData.SysMemPitch = iboDesc.ByteWidth;
221 iboData.SysMemSlicePitch = iboDesc.ByteWidth;
222
223 if (FAILED(m_device->CreateBuffer(&iboDesc, &iboData, &m_ibo))) {
224 std::cerr << "Failed to create index buffer" << std::endl;
225 return;
226 }
227
228 D3D11_BUFFER_DESC cbDesc;
229 cbDesc.ByteWidth = sizeof(PsConstants);
230 cbDesc.Usage = D3D11_USAGE_DYNAMIC;
231 cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
232 cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
233 cbDesc.MiscFlags = 0;
234 cbDesc.StructureByteStride = 0;
235
236 if (FAILED(m_device->CreateBuffer(&cbDesc, nullptr, &m_cbPs))) {
237 std::cerr << "Failed to create constant buffer" << std::endl;
238 return;
239 }
240
241 cbDesc.ByteWidth = sizeof(VsConstantsPad) * 128 * 8;
242
243 if (FAILED(m_device->CreateBuffer(&cbDesc, nullptr, &m_cbVs))) {
244 std::cerr << "Failed to create constant buffer" << std::endl;
245 return;
246 }
247
248 std::array<uint32_t, 2> colors = { 0xFFFFFFFF, 0xFFC0C0C0 };
249
250 D3D11_SUBRESOURCE_DATA texData;
251 texData.pSysMem = &colors[0];
252 texData.SysMemPitch = sizeof(colors[0]);
253 texData.SysMemSlicePitch = sizeof(colors[0]);
254
255 D3D11_TEXTURE2D_DESC texDesc;
256 texDesc.Width = 1;
257 texDesc.Height = 1;
258 texDesc.MipLevels = 1;
259 texDesc.ArraySize = 1;
260 texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
261 texDesc.SampleDesc = { 1, 0 };
262 texDesc.Usage = D3D11_USAGE_IMMUTABLE;
263 texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
264 texDesc.CPUAccessFlags = 0;
265 texDesc.MiscFlags = 0;
266
267 if (FAILED(m_device->CreateTexture2D(&texDesc, &texData, &m_tex0))) {
268 std::cerr << "Failed to create texture" << std::endl;
269 return;
270 }
271
272 texData.pSysMem = &colors[1];
273
274 if (FAILED(m_device->CreateTexture2D(&texDesc, &texData, &m_tex1))) {
275 std::cerr << "Failed to create texture" << std::endl;
276 return;
277 }
278
279 if (FAILED(m_device->CreateShaderResourceView(m_tex0.ptr(), nullptr, &m_srv0))
280 || FAILED(m_device->CreateShaderResourceView(m_tex1.ptr(), nullptr, &m_srv1))) {
281 std::cerr << "Failed to create SRV" << std::endl;
282 return;
283 }
284
285 m_initialized = true;
286 }
287
288
289 ~TriangleApp() {
290 m_context->ClearState();
291 }
292
293
294 bool run() {
295 if (!m_initialized)
296 return false;
297
298 if (m_occluded && (m_occluded = isOccluded()))
299 return true;
300
301 if (!beginFrame())
302 return true;
303
304 std::array<PsConstants, 2> colors = {{
305 PsConstants { 0.25f, 0.25f, 0.25f, 1.0f },
306 PsConstants { 0.40f, 0.40f, 0.40f, 1.0f },
307 }};
308
309 for (uint32_t i = 0; i < 8; i++) {
310 DrawOptions options;
311 options.sortByTexture = i & 1;
312 options.drawIndexed = i & 2;
313 options.mapDiscardOnce = i & 4;
314 drawLines(colors[i & 1], options, i);
315 }
316
317 if (!endFrame())
318 return false;
319
320 updateFps();
321 return true;
322 }
323
324
325 void drawLines(const PsConstants& psData, const DrawOptions& options, uint32_t baseY) {
326 D3D11_MAPPED_SUBRESOURCE sr;
327
328 // Update color for the row
329 m_context->PSSetConstantBuffers(0, 1, &m_cbPs);
330 m_context->Map(m_cbPs.ptr(), 0, D3D11_MAP_WRITE_DISCARD, 0, &sr);
331 std::memcpy(sr.pData, &psData, sizeof(psData));
332 m_context->Unmap(m_cbPs.ptr(), 0);
333
334 baseY *= 8;
335
336 if (options.mapDiscardOnce) {
337 uint32_t drawIndex = 0;
338
339 // Discard and map the entire vertex constant buffer
340 // once, then bind sub-ranges while emitting draw calls
341 m_context->Map(m_cbVs.ptr(), 0, D3D11_MAP_WRITE_DISCARD, 0, &sr);
342 auto vsData = reinterpret_cast<VsConstantsPad*>(sr.pData);
343
344 for (uint32_t y = 0; y < 8; y++) {
345 for (uint32_t x = 0; x < 128; x++)
346 vsData[drawIndex++].data = getVsConstants(x, baseY + y);
347 }
348
349 m_context->Unmap(m_cbVs.ptr(), 0);
350 }
351
352 if (options.drawIndexed)
353 m_context->IASetIndexBuffer(m_ibo.ptr(), DXGI_FORMAT_R32_UINT, 0);
354
355 uint32_t vsStride = sizeof(Vertex);
356 uint32_t vsOffset = 0;
357 m_context->IASetVertexBuffers(0, 1, &m_vbo, &vsStride, &vsOffset);
358
359 uint32_t maxZ = options.sortByTexture ? 2 : 1;
360
361 for (uint32_t z = 0; z < maxZ; z++) {
362 uint32_t drawIndex = z;
363
364 if (options.sortByTexture) {
365 ID3D11ShaderResourceView* view = z ? m_srv1.ptr() : m_srv0.ptr();
366 m_context->PSSetShaderResources(0, 1, &view);
367 }
368
369 for (uint32_t y = 0; y < 8; y++) {
370 for (uint32_t x = z; x < 128; x += maxZ) {
371 uint32_t triIndex = (x ^ y) & 1;
372
373 if (!options.mapDiscardOnce) {
374 D3D11_MAP mapMode = drawIndex ? D3D11_MAP_WRITE_NO_OVERWRITE : D3D11_MAP_WRITE_DISCARD;
375 m_context->Map(m_cbVs.ptr(), 0, mapMode, 0, &sr);
376 auto vsData = reinterpret_cast<VsConstantsPad*>(sr.pData);
377 vsData[drawIndex].data = getVsConstants(x, baseY + y);
378 m_context->Unmap(m_cbVs.ptr(), 0);
379 }
380
381 uint32_t constantOffset = 16 * drawIndex;
382 uint32_t constantCount = 16;
383 m_context->VSSetConstantBuffers1(0, 1, &m_cbVs, &constantOffset, &constantCount);
384
385 if (!options.sortByTexture) {
386 ID3D11ShaderResourceView* view = triIndex ? m_srv1.ptr() : m_srv0.ptr();
387 m_context->PSSetShaderResources(0, 1, &view);
388 }
389
390 // Submit draw call
391 uint32_t baseIndex = 3 * triIndex;
392
393 if (options.drawIndexed)
394 m_context->DrawIndexed(3, baseIndex, 0);
395 else
396 m_context->Draw(3, baseIndex);
397
398 drawIndex += maxZ;
399 }
400 }
401 }
402 }
403
404
405 static VsConstants getVsConstants(uint32_t x, uint32_t y) {
406 VsConstants result;
407 result.x = float(x) / 128.0f;
408 result.y = float(y) / 64.0f;
409 result.w = 1.0f / 128.0f;
410 result.h = 1.0f / 64.0f;
411 return result;
412 }
413
414
415 bool beginFrame() {
416 // Make sure we can actually render to the window
417 RECT windowRect = { 0, 0, 1024, 600 };
418 GetClientRect(m_window, &windowRect);
419
420 uint32_t newWindowSizeW = uint32_t(windowRect.right - windowRect.left);
421 uint32_t newWindowSizeH = uint32_t(windowRect.bottom - windowRect.top);
422
423 if (m_windowSizeW != newWindowSizeW || m_windowSizeH != newWindowSizeH) {
424 m_rtv = nullptr;
425 m_context->ClearState();
426
427 DXGI_SWAP_CHAIN_DESC1 desc;
428 m_swapChain->GetDesc1(&desc);
429
430 if (FAILED(m_swapChain->ResizeBuffers(desc.BufferCount,
431 newWindowSizeW, newWindowSizeH, desc.Format, desc.Flags))) {
432 std::cerr << "Failed to resize back buffers" << std::endl;
433 return false;
434 }
435
436 Com<ID3D11Texture2D> backBuffer;
437 if (FAILED(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer)))) {
438 std::cerr << "Failed to get swap chain back buffer" << std::endl;
439 return false;
440 }
441
442 D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
443 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
444 rtvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
445 rtvDesc.Texture2D = { 0u };
446
447 if (FAILED(m_device->CreateRenderTargetView(backBuffer.ptr(), &rtvDesc, &m_rtv))) {
448 std::cerr << "Failed to create render target view" << std::endl;
449 return false;
450 }
451
452 m_windowSizeW = newWindowSizeW;
453 m_windowSizeH = newWindowSizeH;
454 }
455
456 // Set up render state
457 FLOAT color[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
458 m_context->OMSetRenderTargets(1, &m_rtv, nullptr);
459 m_context->ClearRenderTargetView(m_rtv.ptr(), color);
460
461 m_context->VSSetShader(m_vs.ptr(), nullptr, 0);
462 m_context->PSSetShader(m_ps.ptr(), nullptr, 0);
463
464 m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
465 m_context->IASetInputLayout(m_vertexFormat.ptr());
466
467 D3D11_VIEWPORT viewport;
468 viewport.TopLeftX = 0.0f;
469 viewport.TopLeftY = 0.0f;
470 viewport.Width = float(m_windowSizeW);
471 viewport.Height = float(m_windowSizeH);
472 viewport.MinDepth = 0.0f;
473 viewport.MaxDepth = 1.0f;
474 m_context->RSSetViewports(1, &viewport);
475 return true;
476 }
477
478
479 bool endFrame() {
480 HRESULT hr = m_swapChain->Present(0, DXGI_PRESENT_TEST);
481
482 if (hr == S_OK)
483 hr = m_swapChain->Present(0, 0);
484
485 m_occluded = hr == DXGI_STATUS_OCCLUDED;
486 return true;
487 }
488
489 void updateFps() {
490 if (!m_qpcFrequency.QuadPart)
491 QueryPerformanceFrequency(&m_qpcFrequency);
492
493 if (!m_qpcLastUpdate.QuadPart)
494 QueryPerformanceCounter(&m_qpcLastUpdate);
495
496 LARGE_INTEGER now;
497 QueryPerformanceCounter(&now);
498
499 m_frameCount++;
500
501 if (now.QuadPart - m_qpcLastUpdate.QuadPart < m_qpcFrequency.QuadPart)
502 return;
503
504 double seconds = double(now.QuadPart - m_qpcLastUpdate.QuadPart) / double(m_qpcFrequency.QuadPart);
505 double fps = double(m_frameCount) / seconds;
506
507 std::wstringstream str;
508 str << L"D3D11 triangle (" << fps << L" FPS)";
509
510 SetWindowTextW(m_window, str.str().c_str());
511
512 m_qpcLastUpdate = now;
513 m_frameCount = 0;
514 }
515
516 bool isOccluded() {
517 return m_swapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED;
518 }
519
520private:
521
522 HWND m_window;
523 uint32_t m_windowSizeW = 1024;
524 uint32_t m_windowSizeH = 600;
525 bool m_initialized = false;
526 bool m_occluded = false;
527
528 Com<IDXGIFactory3> m_factory;
529 Com<IDXGIAdapter> m_adapter;
530 Com<ID3D11Device1> m_device;
531 Com<ID3D11DeviceContext1> m_context;
532 Com<IDXGISwapChain2> m_swapChain;
533
534 Com<ID3D11RenderTargetView> m_rtv;
535 Com<ID3D11Buffer> m_ibo;
536 Com<ID3D11Buffer> m_vbo;
537 Com<ID3D11InputLayout> m_vertexFormat;
538
539 Com<ID3D11Texture2D> m_tex0;
540 Com<ID3D11Texture2D> m_tex1;
541 Com<ID3D11ShaderResourceView> m_srv0;
542 Com<ID3D11ShaderResourceView> m_srv1;
543
544 Com<ID3D11Buffer> m_cbPs;
545 Com<ID3D11Buffer> m_cbVs;
546
547 Com<ID3D11VertexShader> m_vs;
548 Com<ID3D11PixelShader> m_ps;
549
550 LARGE_INTEGER m_qpcLastUpdate = { };
551 LARGE_INTEGER m_qpcFrequency = { };
552
553 uint32_t m_frameCount = 0;
554
555};
556
557LRESULT CALLBACK WindowProc(HWND hWnd,
558 UINT message,
559 WPARAM wParam,
560 LPARAM lParam);
561
562int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
563 WNDCLASSEXW wc = { };
564 wc.cbSize = sizeof(wc);
565 wc.style = CS_HREDRAW | CS_VREDRAW;
566 wc.lpfnWndProc = WindowProc;
567 wc.hInstance = hInstance;
568 wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
569 wc.hbrBackground = HBRUSH(COLOR_WINDOW);
570 wc.lpszClassName = L"WindowClass";
571 RegisterClassExW(&wc);
572
573 HWND hWnd = CreateWindowExW(0, L"WindowClass", L"D3D11 triangle",
574 WS_OVERLAPPEDWINDOW, 300, 300, 1024, 600,
575 nullptr, nullptr, hInstance, nullptr);
576 ShowWindow(hWnd, nCmdShow);
577
578 TriangleApp app(hInstance, hWnd);
579
580 MSG msg;
581
582 while (true) {
583 if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
584 TranslateMessage(&msg);
585 DispatchMessageW(&msg);
586
587 if (msg.message == WM_QUIT)
588 return msg.wParam;
589 } else {
590 if (!app.run())
591 break;
592 }
593 }
594
595 return msg.wParam;
596}
597
598LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
599 switch (message) {
600 case WM_CLOSE:
601 PostQuitMessage(0);
602 return 0;
603 }
604
605 return DefWindowProcW(hWnd, message, wParam, lParam);
606}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette