From c6430374f4fac322fea55d50a1cc457f15451d49 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Wed, 26 Feb 2025 08:34:17 -0800 Subject: [PATCH 1/7] web: redo debugger styling How long can I screw around with debugger styling? Apparently at least one more hour... --- web/debugger.html | 10 ++- web/debugger/break.tsx | 4 +- web/debugger/code.tsx | 8 +- web/debugger/debugger.tsx | 148 +++++++++++++++++++------------------ web/debugger/mappings.tsx | 6 +- web/debugger/memory.tsx | 4 +- web/debugger/registers.tsx | 104 +++++++++++++------------- web/debugger/stack.tsx | 12 +-- web/debugger/tabs.tsx | 4 +- web/win2k.css | 6 +- 10 files changed, 152 insertions(+), 154 deletions(-) diff --git a/web/debugger.html b/web/debugger.html index 8a935f5ab..77657213c 100644 --- a/web/debugger.html +++ b/web/debugger.html @@ -18,14 +18,18 @@ } body { + margin: 0; flex: 1; display: flex; flex-direction: column; - gap: 8px; + min-width: 0; } - section.panel { + section { padding: 1ex; + background: var(--gray); + border-right: 1px solid black; + border-bottom: 1px solid black; } /* memory */ @@ -46,13 +50,11 @@ outline: solid 1px red; } - .error { color: red; } .code { - padding: 1ex; background: white; width: 80ex; } diff --git a/web/debugger/break.tsx b/web/debugger/break.tsx index cffd33a34..7a4068732 100644 --- a/web/debugger/break.tsx +++ b/web/debugger/break.tsx @@ -137,10 +137,10 @@ export class BreakpointsComponent extends preact.Component +
{rows} this.add(text)} /> - +
); } } diff --git a/web/debugger/code.tsx b/web/debugger/code.tsx index c94b0ffbe..469726ce9 100644 --- a/web/debugger/code.tsx +++ b/web/debugger/code.tsx @@ -53,11 +53,9 @@ export class Code extends preact.Component { ); }); return ( -
- - {instrs} - -
+ + {instrs} + ); } } diff --git a/web/debugger/debugger.tsx b/web/debugger/debugger.tsx index 0200c23be..a96a33d18 100644 --- a/web/debugger/debugger.tsx +++ b/web/debugger/debugger.tsx @@ -182,34 +182,23 @@ export class Debugger extends preact.Component { const { emulator, labels } = this.state; const output = ( -
- - {this.state.stdout} - {this.state.error ?
ERROR: {this.state.error}
: null} -
-
+ + {this.state.stdout} + {this.state.error ?
ERROR: {this.state.error}
: null} +
); if (!emulator) { return output; } - // Note: disassemble_json() may cause allocations, invalidating any existing .memory()! + // Note: disassemble() may cause allocations, invalidating any existing .memory()! let instrs: Instruction[] = []; - let code; const eip = emulator.emu.eip; if (eip == 0xffff_fff0) { - code =
(in async)
; + instrs = []; } else { instrs = emulator.disassemble(eip); - code = ( - - ); } return ( <> @@ -227,66 +216,79 @@ export class Debugger extends preact.Component { {emulator.emu.instr_count} instrs executed | {Math.floor(emulator.looper.stepsPerMs)}/ms -
- {code} - - +
+
+ +
+
+ +
+
+ +
- output, +
+ output, - memory: () => ( - this.setState({ memBase: Math.max(0, addr) })} - /> - ), - mappings: () => ( - - ), + memory: () => ( + this.setState({ memBase: Math.max(0, addr) })} + /> + ), + mappings: () => ( + + ), - imports: () => { - const labels = emulator.labels(); - return ( -
- {labels.map(([addr, name]) => ( -
- - {addr}: {name} - -
- ))} -
- ); - }, + imports: () => { + const labels = emulator.labels(); + return ( +
+ {labels.map(([addr, name]) => ( +
+ + {addr}: {name} + +
+ ))} +
+ ); + }, - breakpoints: () => ( - - ), - }} - selected={this.state.selectedTab} - switchTab={(selectedTab) => this.setState({ selectedTab })} - /> + breakpoints: () => ( + + ), + }} + selected={this.state.selectedTab} + switchTab={(selectedTab) => this.setState({ selectedTab })} + /> +
); } diff --git a/web/debugger/mappings.tsx b/web/debugger/mappings.tsx index d716d9d60..7eb951885 100644 --- a/web/debugger/mappings.tsx +++ b/web/debugger/mappings.tsx @@ -33,8 +33,8 @@ export class Mappings extends preact.Component { ); }); return ( -
- +
+
@@ -44,7 +44,7 @@ export class Mappings extends preact.Component { {rows}
addr
-
+
); } } diff --git a/web/debugger/memory.tsx b/web/debugger/memory.tsx index eed643f5a..bb1492c9c 100644 --- a/web/debugger/memory.tsx +++ b/web/debugger/memory.tsx @@ -114,7 +114,7 @@ export class Memory extends preact.Component { } return ( -
+
@@ -125,7 +125,7 @@ export class Memory extends preact.Component { {hexRows} {asciiRows}
-
+ ); } } diff --git a/web/debugger/registers.tsx b/web/debugger/registers.tsx index dd24479cb..d9f1d26c0 100644 --- a/web/debugger/registers.tsx +++ b/web/debugger/registers.tsx @@ -14,63 +14,61 @@ export class RegistersComponent extends preact.Component - -
- eax {regs.eax} -
- ebx {regs.ebx} -
- ecx {regs.ecx} -
- edx {regs.edx} -
-
+ +
+ eax {regs.eax}
-
- eip {regs.eip} -
- esp {regs.esp} -
- ebp {regs.ebp} -
- esi {regs.esi} -
- edi {regs.edi} -
-
+ ebx {regs.ebx}
-
- cs {regs.cs}{' '} - fs {regs.fs} -
- ds {regs.ds}{' '} - gs {regs.gs} -
- es {regs.es}{' '} - ss {regs.ss} -
-
+ ecx {regs.ecx}
-
- flags {hex(regs.flags)} {regs.flags_str} -
+ edx {regs.edx}
- {st.length > 0 - ? ( -
- fpu
- {Array.from(regs.st).map(n => ( - - {n} -
-
- ))} -
- ) - : null} -
- +
+
+
+ eip {regs.eip} +
+ esp {regs.esp} +
+ ebp {regs.ebp} +
+ esi {regs.esi} +
+ edi {regs.edi} +
+
+
+
+ cs {regs.cs}{' '} + fs {regs.fs} +
+ ds {regs.ds}{' '} + gs {regs.gs} +
+ es {regs.es}{' '} + ss {regs.ss} +
+
+
+
+ flags {hex(regs.flags)} {regs.flags_str} +
+
+ {st.length > 0 + ? ( +
+ fpu
+ {Array.from(regs.st).map(n => ( + + {n} +
+
+ ))} +
+ ) + : null} +
); } } diff --git a/web/debugger/stack.tsx b/web/debugger/stack.tsx index 4d6f0f9d9..ba428eae9 100644 --- a/web/debugger/stack.tsx +++ b/web/debugger/stack.tsx @@ -18,15 +18,13 @@ export class Stack extends preact.Component { const rows = []; for (let addr = esp - 0x10; addr < esp + 0x20; addr += 4) { const value = memory.getUint32(addr, true); - let label = this.props.labels.get(value); - if (label) { - label = ` ${label}`; - } + const label = this.props.labels.get(value); let row = (
{addr}   {value} +   {label}
); @@ -35,10 +33,6 @@ export class Stack extends preact.Component { } rows.push(row); } - return ( -
- {rows} -
- ); + return {rows}; } } diff --git a/web/debugger/tabs.tsx b/web/debugger/tabs.tsx index 0620a74e4..1b8365921 100644 --- a/web/debugger/tabs.tsx +++ b/web/debugger/tabs.tsx @@ -14,7 +14,7 @@ export class Tabs extends preact.Component { const { style, tabs, selected, switchTab } = this.props; const content = tabs[selected](); return ( -
+
| {Object.keys(tabs).map((name) => { @@ -26,7 +26,7 @@ export class Tabs extends preact.Component { })}
{content} -
+ ); } } diff --git a/web/win2k.css b/web/win2k.css index 615de3fff..8cca45355 100644 --- a/web/win2k.css +++ b/web/win2k.css @@ -1,3 +1,7 @@ +:root { + --gray: #d6d3ce; +} + body { font-family: tahoma, sans-serif; font-size: 12px; @@ -11,7 +15,7 @@ button { button, .panel { - background: #d0d0c8; + background: var(--gray); border: outset 2px; image-rendering: pixelated; border-image: url("9p-inactive.png") 2; From 91cc62cfb922c51ee571fd36ae05794126929841 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Wed, 26 Feb 2025 19:31:00 -0800 Subject: [PATCH 2/7] web: some design notes on canvas rendering --- web/glue/src/host.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/web/glue/src/host.rs b/web/glue/src/host.rs index f88aea37e..d23e4c4ab 100644 --- a/web/glue/src/host.rs +++ b/web/glue/src/host.rs @@ -4,6 +4,18 @@ use anyhow::bail; use wasm_bindgen::prelude::*; use win32::{Stat, StatKind, WindowsPath}; +/// We create one per DirectDraw Surface. We draw to them by pushing pixels +/// via putImageData (not hw accelerated) and then use drawImage (likely hw accelerated) +/// to implement bit_blt between surfaces. +/// +/// We also use drawImage to draw the "primary" surface to the canvas in the HTML DOM +/// when page flipping. It's not clear whether this performs well vs just putting the +/// relevant canvas in place in the DOM, nor can I find good references that talk about +/// these relative options. FWIW ChatGPT believes that drawImage is faster than DOM +/// manipulation. +/// +/// https://developer.chrome.com/blog/taking-advantage-of-gpu-acceleration-in-the-2d-canvas +/// https://web.dev/articles/canvas-performance struct WebSurface { _hwnd: u32, canvas: web_sys::HtmlCanvasElement, From 2ccb634f8652f040b8676e5a582c266dd49cc715 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Wed, 26 Feb 2025 19:41:03 -0800 Subject: [PATCH 3/7] web: begin exposing directdraw state --- web/debugger/ddraw.tsx | 36 ++++++++++++++++++++++++++++++++++++ web/debugger/debugger.tsx | 3 +++ web/glue/src/debugger.rs | 36 ++++++++++++++++++++++++++++++++++++ web/glue/src/emulator.rs | 4 ++++ 4 files changed, 79 insertions(+) create mode 100644 web/debugger/ddraw.tsx diff --git a/web/debugger/ddraw.tsx b/web/debugger/ddraw.tsx new file mode 100644 index 000000000..72b16e6cb --- /dev/null +++ b/web/debugger/ddraw.tsx @@ -0,0 +1,36 @@ +import * as preact from 'preact'; +import { Fragment, h } from 'preact'; +import * as wasm from '../glue/pkg/glue'; + +namespace DirectDraw { + export interface Props { + state: wasm.DirectDrawState; + } +} +export class DirectDraw extends preact.Component { + render() { + const rows = this.props.state.surfaces.map((surface) => { + return ( + + {surface.width} + {surface.height} + {surface.bytes_per_pixel} + {surface.primary ? 'yes' : 'no'} + + ); + }); + return ( +
+ + + + + + + + {rows} +
widthheightbytes_per_pixelprimary
+
+ ); + } +} diff --git a/web/debugger/debugger.tsx b/web/debugger/debugger.tsx index a96a33d18..b1e355d4d 100644 --- a/web/debugger/debugger.tsx +++ b/web/debugger/debugger.tsx @@ -5,6 +5,7 @@ import { Instruction } from '../glue/pkg/glue'; import { EmulatorComponent } from '../web'; import { BreakpointsComponent } from './break'; import { Code } from './code'; +import { DirectDraw } from './ddraw'; import { Labels, parseCSV } from './labels'; import { Mappings } from './mappings'; import { Memory, MemoryView, Number } from './memory'; @@ -284,6 +285,8 @@ export class Debugger extends preact.Component { {...this.memoryView} /> ), + + directdraw: () => , }} selected={this.state.selectedTab} switchTab={(selectedTab) => this.setState({ selectedTab })} diff --git a/web/glue/src/debugger.rs b/web/glue/src/debugger.rs index 09cad8ba6..a1e1c28e8 100644 --- a/web/glue/src/debugger.rs +++ b/web/glue/src/debugger.rs @@ -50,3 +50,39 @@ impl Registers { } } } + +#[derive(Tsify, serde::Serialize)] +pub struct DirectDrawSurface { + pub width: u32, + pub height: u32, + pub bytes_per_pixel: u32, + pub primary: bool, + // TODO: + // pub palette: Option, + // pixels: u32, + // pub attached: u32, +} + +#[derive(Tsify, serde::Serialize)] +#[tsify(into_wasm_abi)] +pub struct DirectDrawState { + surfaces: Vec, +} + +impl DirectDrawState { + pub fn from_machine(machine: &win32::Machine) -> DirectDrawState { + let ddraw = &machine.state.ddraw; + DirectDrawState { + surfaces: ddraw + .surfaces + .values() + .map(|s| DirectDrawSurface { + width: s.width, + height: s.height, + bytes_per_pixel: s.bytes_per_pixel, + primary: s.primary, + }) + .collect(), + } + } +} diff --git a/web/glue/src/emulator.rs b/web/glue/src/emulator.rs index aaf67ea25..803163840 100644 --- a/web/glue/src/emulator.rs +++ b/web/glue/src/emulator.rs @@ -123,6 +123,10 @@ impl Emulator { pub fn set_tracing_scheme(&self, scheme: &str) { win32::winapi::trace::set_scheme(scheme); } + + pub fn directdraw_state(&self) -> debugger::DirectDrawState { + debugger::DirectDrawState::from_machine(&self.machine) + } } #[wasm_bindgen] From fbdf8de676e702ba1a48dbc40b503f5f8fb209db Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Thu, 27 Feb 2025 09:25:50 -0800 Subject: [PATCH 4/7] web: better preact import --- web/debugger/break.tsx | 1 - web/debugger/code.tsx | 1 - web/debugger/ddraw.tsx | 1 - web/debugger/debugger.tsx | 1 - web/debugger/mappings.tsx | 1 - web/debugger/memory.tsx | 1 - web/debugger/registers.tsx | 1 - web/debugger/stack.tsx | 1 - web/debugger/tabs.tsx | 1 - web/run.tsx | 1 - web/tsconfig.json | 12 ++---------- web/web.tsx | 1 - 12 files changed, 2 insertions(+), 21 deletions(-) diff --git a/web/debugger/break.tsx b/web/debugger/break.tsx index 7a4068732..2d70c144a 100644 --- a/web/debugger/break.tsx +++ b/web/debugger/break.tsx @@ -1,5 +1,4 @@ import * as preact from 'preact'; -import { h } from 'preact'; import * as wasm from '../glue/pkg/glue'; import { Labels } from './labels'; import { MemoryView, Number } from './memory'; diff --git a/web/debugger/code.tsx b/web/debugger/code.tsx index 469726ce9..ae3f6cd9a 100644 --- a/web/debugger/code.tsx +++ b/web/debugger/code.tsx @@ -1,5 +1,4 @@ import * as preact from 'preact'; -import { Fragment, h } from 'preact'; import { Instruction } from '../glue/pkg/glue'; import { Labels } from './labels'; import { MemoryView, Number } from './memory'; diff --git a/web/debugger/ddraw.tsx b/web/debugger/ddraw.tsx index 72b16e6cb..9b7f4183e 100644 --- a/web/debugger/ddraw.tsx +++ b/web/debugger/ddraw.tsx @@ -1,5 +1,4 @@ import * as preact from 'preact'; -import { Fragment, h } from 'preact'; import * as wasm from '../glue/pkg/glue'; namespace DirectDraw { diff --git a/web/debugger/debugger.tsx b/web/debugger/debugger.tsx index b1e355d4d..0e7022af1 100644 --- a/web/debugger/debugger.tsx +++ b/web/debugger/debugger.tsx @@ -1,5 +1,4 @@ import * as preact from 'preact'; -import { Fragment, h } from 'preact'; import * as emulator from '../emulator'; import { Instruction } from '../glue/pkg/glue'; import { EmulatorComponent } from '../web'; diff --git a/web/debugger/mappings.tsx b/web/debugger/mappings.tsx index 7eb951885..09c49866b 100644 --- a/web/debugger/mappings.tsx +++ b/web/debugger/mappings.tsx @@ -1,5 +1,4 @@ import * as preact from 'preact'; -import { h } from 'preact'; import * as wasm from '../glue/pkg/glue'; import { MemoryView, Number } from './memory'; import { hex } from './util'; diff --git a/web/debugger/memory.tsx b/web/debugger/memory.tsx index bb1492c9c..6b12dc5f3 100644 --- a/web/debugger/memory.tsx +++ b/web/debugger/memory.tsx @@ -1,5 +1,4 @@ import * as preact from 'preact'; -import { h } from 'preact'; import { hex } from './util'; /** diff --git a/web/debugger/registers.tsx b/web/debugger/registers.tsx index d9f1d26c0..50891f408 100644 --- a/web/debugger/registers.tsx +++ b/web/debugger/registers.tsx @@ -1,5 +1,4 @@ import * as preact from 'preact'; -import { h } from 'preact'; import { Registers } from '../glue/pkg/glue'; import { MemoryView, Number } from './memory'; import { hex } from './util'; diff --git a/web/debugger/stack.tsx b/web/debugger/stack.tsx index ba428eae9..23fcc1bf3 100644 --- a/web/debugger/stack.tsx +++ b/web/debugger/stack.tsx @@ -1,5 +1,4 @@ import * as preact from 'preact'; -import { h } from 'preact'; import { Emulator } from '../glue/pkg/glue'; import { Labels } from './labels'; import { MemoryView, Number } from './memory'; diff --git a/web/debugger/tabs.tsx b/web/debugger/tabs.tsx index 1b8365921..dfcd20f83 100644 --- a/web/debugger/tabs.tsx +++ b/web/debugger/tabs.tsx @@ -1,5 +1,4 @@ import * as preact from 'preact'; -import { Fragment, h } from 'preact'; namespace Tabs { export interface Props { diff --git a/web/run.tsx b/web/run.tsx index 61e0f69a0..71835bd99 100644 --- a/web/run.tsx +++ b/web/run.tsx @@ -4,7 +4,6 @@ */ import * as preact from 'preact'; -import { Fragment, h } from 'preact'; import * as emulator from './emulator'; import { EmulatorComponent } from './web'; diff --git a/web/tsconfig.json b/web/tsconfig.json index 7d53ab859..b211f5d80 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -1,7 +1,6 @@ { "compilerOptions": { /* Visit https://aka.ms/tsconfig to read more about this file */ - /* Projects */ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ @@ -9,21 +8,19 @@ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - /* Language and Environment */ "target": "ES2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ "jsx": "react", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - "jsxFactory": "h", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - "jsxFragmentFactory": "Fragment", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + "jsxFactory": "preact.h", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + "jsxFragmentFactory": "preact.Fragment", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - /* Modules */ "module": "ES2022", /* Specify what module code is generated. */ // "rootDir": "./", /* Specify the root folder within your source files. */ @@ -37,12 +34,10 @@ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ // "resolveJsonModule": true, /* Enable importing .json files. */ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - /* JavaScript Support */ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - /* Emit */ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ // "declarationMap": true, /* Create sourcemaps for d.ts files. */ @@ -67,14 +62,12 @@ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - /* Interop Constraints */ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - /* Type Checking */ "strict": true, /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ @@ -95,7 +88,6 @@ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ diff --git a/web/web.tsx b/web/web.tsx index ee4f49027..d8c4e9de1 100644 --- a/web/web.tsx +++ b/web/web.tsx @@ -3,7 +3,6 @@ */ import * as preact from 'preact'; -import { Fragment, h } from 'preact'; import { Emulator } from './emulator'; namespace WindowComponent { From 9e723999dfbff350d6c49eca59958bebd1ca0553 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Thu, 27 Feb 2025 09:48:45 -0800 Subject: [PATCH 5/7] ddraw: minor clarifications --- win32/src/winapi/ddraw/ddraw.rs | 13 +++++++++---- win32/src/winapi/ddraw/ddraw7.rs | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/win32/src/winapi/ddraw/ddraw.rs b/win32/src/winapi/ddraw/ddraw.rs index fdd53d28d..c5c9be22d 100644 --- a/win32/src/winapi/ddraw/ddraw.rs +++ b/win32/src/winapi/ddraw/ddraw.rs @@ -77,7 +77,7 @@ impl Surface { } if opts.bytes_per_pixel == 0 { - opts.bytes_per_pixel = machine.state.ddraw.bytes_per_pixel; + opts.bytes_per_pixel = machine.state.ddraw.screen_bytes_per_pixel; } surfaces.push(Surface::new(machine, hwnd, &opts)); @@ -202,11 +202,16 @@ impl Surface { } pub struct State { - // TODO: this is per-IDirectDraw state. + // TODO: the fields in this struct are really per-IDirectDraw state. pub hwnd: HWND, + + /// Maps interface pointer to Surface objects. + /// Note that you cannot have multiple pointers to the same surface; + /// instead, DirectDraw refcounts the interfaces themselves. pub surfaces: HashMap, - pub bytes_per_pixel: u32, + /// bpp of the current display mode. + pub screen_bytes_per_pixel: u32, pub palettes: HashMap, } @@ -216,7 +221,7 @@ impl Default for State { State { hwnd: HWND::null(), surfaces: HashMap::new(), - bytes_per_pixel: 4, + screen_bytes_per_pixel: 4, palettes: HashMap::new(), } } diff --git a/win32/src/winapi/ddraw/ddraw7.rs b/win32/src/winapi/ddraw/ddraw7.rs index 3b5c21a1b..4b0f4ae92 100644 --- a/win32/src/winapi/ddraw/ddraw7.rs +++ b/win32/src/winapi/ddraw/ddraw7.rs @@ -287,7 +287,7 @@ pub mod IDirectDraw7 { wnd.borrow_mut() .set_client_size(&mut *machine.host, width, height); } - machine.state.ddraw.bytes_per_pixel = bpp / 8; + machine.state.ddraw.screen_bytes_per_pixel = bpp / 8; DD::OK } From 41c728ed12a4fbf6ad74fe19129c09782cd53bcb Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Thu, 27 Feb 2025 09:55:59 -0800 Subject: [PATCH 6/7] ddraw: debugger shows surface ptrs --- web/debugger/ddraw.tsx | 15 ++++++++++----- web/debugger/debugger.tsx | 2 +- web/glue/src/debugger.rs | 6 ++++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/web/debugger/ddraw.tsx b/web/debugger/ddraw.tsx index 9b7f4183e..55ed1122d 100644 --- a/web/debugger/ddraw.tsx +++ b/web/debugger/ddraw.tsx @@ -1,8 +1,9 @@ import * as preact from 'preact'; import * as wasm from '../glue/pkg/glue'; +import { MemoryView, Number } from './memory'; namespace DirectDraw { - export interface Props { + export interface Props extends MemoryView { state: wasm.DirectDrawState; } } @@ -11,10 +12,13 @@ export class DirectDraw extends preact.Component { const rows = this.props.state.surfaces.map((surface) => { return ( - {surface.width} - {surface.height} - {surface.bytes_per_pixel} - {surface.primary ? 'yes' : 'no'} + + {surface.ptr} + + {surface.width} + {surface.height} + {surface.bytes_per_pixel} + {surface.primary ? 'yes' : 'no'} ); }); @@ -22,6 +26,7 @@ export class DirectDraw extends preact.Component {
+ diff --git a/web/debugger/debugger.tsx b/web/debugger/debugger.tsx index 0e7022af1..61b0e4ec2 100644 --- a/web/debugger/debugger.tsx +++ b/web/debugger/debugger.tsx @@ -285,7 +285,7 @@ export class Debugger extends preact.Component { /> ), - directdraw: () => , + directdraw: () => , }} selected={this.state.selectedTab} switchTab={(selectedTab) => this.setState({ selectedTab })} diff --git a/web/glue/src/debugger.rs b/web/glue/src/debugger.rs index a1e1c28e8..1db8016a6 100644 --- a/web/glue/src/debugger.rs +++ b/web/glue/src/debugger.rs @@ -53,6 +53,7 @@ impl Registers { #[derive(Tsify, serde::Serialize)] pub struct DirectDrawSurface { + pub ptr: u32, pub width: u32, pub height: u32, pub bytes_per_pixel: u32, @@ -75,8 +76,9 @@ impl DirectDrawState { DirectDrawState { surfaces: ddraw .surfaces - .values() - .map(|s| DirectDrawSurface { + .iter() + .map(|(&ptr, s)| DirectDrawSurface { + ptr, width: s.width, height: s.height, bytes_per_pixel: s.bytes_per_pixel, From 35fb2dd0bc62d75846da74a9eaf62d0257563f38 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Thu, 27 Feb 2025 09:43:09 -0800 Subject: [PATCH 7/7] ddraw: show canvas inline in debugger --- web/debugger/ddraw.tsx | 20 +++++++++++--- web/debugger/debugger.tsx | 2 +- web/glue/src/debugger.rs | 58 +++++++++++++++++++++++---------------- web/glue/src/emulator.rs | 4 +-- web/glue/src/host.rs | 4 +-- 5 files changed, 55 insertions(+), 33 deletions(-) diff --git a/web/debugger/ddraw.tsx b/web/debugger/ddraw.tsx index 55ed1122d..c7dd09059 100644 --- a/web/debugger/ddraw.tsx +++ b/web/debugger/ddraw.tsx @@ -4,14 +4,25 @@ import { MemoryView, Number } from './memory'; namespace DirectDraw { export interface Props extends MemoryView { - state: wasm.DirectDrawState; + surfaces: wasm.SurfaceDebug[]; + } + export interface State { + hover?: wasm.SurfaceDebug; } } -export class DirectDraw extends preact.Component { +export class DirectDraw extends preact.Component { + canvasContainer = (parent: HTMLElement | null) => { + if (!parent) return; + parent.appendChild(this.state.hover!.canvas); + }; + render() { - const rows = this.props.state.surfaces.map((surface) => { + const rows = this.props.surfaces.map((surface) => { return ( - + this.setState({ hover: surface })} + onMouseLeave={() => this.setState({ hover: undefined })} + > @@ -34,6 +45,7 @@ export class DirectDraw extends preact.Component { {rows}
ptr width height bytes_per_pixel
{surface.ptr}
+ {this.state.hover &&
}
); } diff --git a/web/debugger/debugger.tsx b/web/debugger/debugger.tsx index 61b0e4ec2..f567308b6 100644 --- a/web/debugger/debugger.tsx +++ b/web/debugger/debugger.tsx @@ -285,7 +285,7 @@ export class Debugger extends preact.Component { /> ), - directdraw: () => , + directdraw: () => , }} selected={this.state.selectedTab} switchTab={(selectedTab) => this.setState({ selectedTab })} diff --git a/web/glue/src/debugger.rs b/web/glue/src/debugger.rs index 1db8016a6..44a6a22d8 100644 --- a/web/glue/src/debugger.rs +++ b/web/glue/src/debugger.rs @@ -1,6 +1,9 @@ //! API used specifically for debugging the emulator. -use tsify::Tsify; +use tsify::{JsValueSerdeExt, Tsify}; +use wasm_bindgen::prelude::*; + +use crate::host::WebSurface; /// Registers are serialized as a JSON blob. #[derive(Tsify, serde::Serialize)] @@ -51,8 +54,13 @@ impl Registers { } } +#[wasm_bindgen(typescript_custom_section)] +const SURFACE_META_TS: &'static str = r#" +export type SurfaceDebug = DirectDrawSurfaceMeta & { canvas: HTMLCanvasElement }; +"#; + #[derive(Tsify, serde::Serialize)] -pub struct DirectDrawSurface { +pub struct DirectDrawSurfaceMeta { pub ptr: u32, pub width: u32, pub height: u32, @@ -64,27 +72,29 @@ pub struct DirectDrawSurface { // pub attached: u32, } -#[derive(Tsify, serde::Serialize)] -#[tsify(into_wasm_abi)] -pub struct DirectDrawState { - surfaces: Vec, -} +pub fn surfaces_from_machine(machine: &win32::Machine) -> Vec { + machine + .state + .ddraw + .surfaces + .iter() + .map(|(&ptr, s)| { + let meta = DirectDrawSurfaceMeta { + ptr, + width: s.width, + height: s.height, + bytes_per_pixel: s.bytes_per_pixel, + primary: s.primary, + }; -impl DirectDrawState { - pub fn from_machine(machine: &win32::Machine) -> DirectDrawState { - let ddraw = &machine.state.ddraw; - DirectDrawState { - surfaces: ddraw - .surfaces - .iter() - .map(|(&ptr, s)| DirectDrawSurface { - ptr, - width: s.width, - height: s.height, - bytes_per_pixel: s.bytes_per_pixel, - primary: s.primary, - }) - .collect(), - } - } + // Attach canvas property to JS object we create from meta. + let val = JsValue::from_serde(&meta).unwrap(); + let web_surface = + unsafe { &*(s.host.as_ref() as *const dyn win32::Surface as *const WebSurface) }; + js_sys::Reflect::set(&val, &"canvas".into(), &web_surface.canvas.clone().into()) + .unwrap(); + + val + }) + .collect() } diff --git a/web/glue/src/emulator.rs b/web/glue/src/emulator.rs index 803163840..e8db275cd 100644 --- a/web/glue/src/emulator.rs +++ b/web/glue/src/emulator.rs @@ -124,8 +124,8 @@ impl Emulator { win32::winapi::trace::set_scheme(scheme); } - pub fn directdraw_state(&self) -> debugger::DirectDrawState { - debugger::DirectDrawState::from_machine(&self.machine) + pub fn direct_draw_surfaces(&self) -> Vec { + debugger::surfaces_from_machine(&self.machine) } } diff --git a/web/glue/src/host.rs b/web/glue/src/host.rs index d23e4c4ab..9b6f57494 100644 --- a/web/glue/src/host.rs +++ b/web/glue/src/host.rs @@ -16,9 +16,9 @@ use win32::{Stat, StatKind, WindowsPath}; /// /// https://developer.chrome.com/blog/taking-advantage-of-gpu-acceleration-in-the-2d-canvas /// https://web.dev/articles/canvas-performance -struct WebSurface { +pub struct WebSurface { _hwnd: u32, - canvas: web_sys::HtmlCanvasElement, + pub canvas: web_sys::HtmlCanvasElement, width: u32, ctx: web_sys::CanvasRenderingContext2d, screen: web_sys::CanvasRenderingContext2d,