Jump to content
  • Advertisement

umbo

Member
  • Content Count

    17
  • Joined

  • Last visited

Community Reputation

132 Neutral

About umbo

  • Rank
    Member

Personal Information

  • Interests
    Programming
  1. Hi, you made a good point. I checked on it just now. Changed the function in: __declspec (noinline) void __cdecl InitLibrary (void) {     DoInit ();     // Make sure we don't come here again.     g_OneTimeInit = 1; } And then checked both the Debug and Release disassemblies (always x64) -> Twins. Then I removed the '__declspec (noinline)' tokens, checked again the disassemblies -> Twins again. I recall reading somewhere that the code optimizer of Visual Studio would be scared away from a function if it found any inline __asm instruction within them (even if a nop). But this was x86 a decade ago when VS2003 was the next best thing. I thought they had evolved since then :-/ Apparently not. What I write in the stubs I'm finding it 1:1 in the disassembly transversal to build and optimizations.     [edit] Wait... this is working in the x64 Release!?! ; file: Stubs64.asm .data extern InitLibrary : proc extern g_OneTimeInit : dword extern g_fp : qword .code public _DoStuff _DoStuff proc     cmp m_OneTimeInit, 0    ; Compare m_OneTimeInit with 0     jne IsInitAlready       ; Jump to IsInitAlready if m_OneTimeInit != 0     ; Preserve the registers before making the call.     push rcx     push rdx     push r8     push r9     push rbx     push rbp     push rdi     push rsi     push rsp     push r12     push r13     push r14     push r15     ;-----------------------------------------------------------------------------     ; 'Microsoft x64 calling convention' specific:     ; --------------------------------------------     ; Each function call requires 32 bytes of 'shadow space' saved onto the stack.     ; This holds true regardless of the signature of the function actually called.     ;-----------------------------------------------------------------------------     sub rsp, 40    ; 32+8=40 ... +8 because call pushes an 8-byte return value on the stack?     call InitLibrary     ; Reclaim the 'shadow space' previously reserved for the call.     add rsp, 40     ; Restore the registers.     pop r15     pop r14     pop r13     pop r12     pop rsp     pop rsi     pop rdi     pop rbp     pop rbx     pop r9     pop r8     pop rdx     pop rcx     IsInitAlready:     jmp qword ptr [g_fp]    ; call the real DoStuff() function.                             ; g_fp holds the address obtained with GetProcAddress() inside InitLibrary(). _DoStuff endp end It does! But I don't get why. If it wasn't for the very push and pop I'm doing, _DoStuff wouldn't have a use for those registers. As for whoever called _DoStuff from the outside, he too is in the x64 world and needs to preserve his own registers before calling _DoStuff. Doesn't this mean that whatever I do (or not) with the registers in _DoStuff can't affect my caller? Similarly, once inside InitLibrary() (which is proven to never be inlined) the registers are managed by the asm code written by the compiler. I have no influence on them, and again whatever happens in there can't affect _DoStuff which sits at a higher level.   I'm not sure I've worded the concept in the best way (I'm not english, btw) but this is all very confusing to me. Where am I wrong?
  2. Hi, I'm tackling a 'Safe Initialization' issue. When my proxy is loaded by the host process, it needs to initialize. This involves a lengthy sequence of libraries loaded, hooks placed on a number of Windows APIs, a few bytes patched in memory (in the host's IAT, for example), the setup of inter process communication. This stuff tends to be destructive when performed in the context of DllMain(). You'll find processes that tolerate it, as well as those which won't (and crash on A/V exception when you're lucky -- on worse things when not). My goal is not to destabilize or hinder the host process, of course, and so I've been thinking of ways to postpone the initialization as much as possible (it must happen outside of DllMain) while ensuring that it occurs before the host makes use of the services exposed by the original dll for which my proxy exists. For a panacea I thought of 'rigging' the stubs of my proxy with the call to init the very library. It's the closest thing to having the host explicitly call my own Initialization function (which can't happen because the host doesn't know my dll). So the host will load my proxy, which is an empty shell until after the host uses any one of the rigged functions exported by my dll. The mechanism is safe and works magnificently well -- when it does, that is. The x64 Debug build is fine, the x64 Release crashes. And here I am, asking for help, because this thing can only be done in assembly, a terrain I barely scratched the surface of. I'm not completely devoid of knowledge on assembly, but if I have to write the equivalent of a prolog or epilog, well... In x86 the callee would take care of managing the stack. Since the callee is a C++ function -void InitLibrary (void)- I can just call it and trust the compiler to do the magic for me. But in x64 the caller is responsible. And the caller is my asm stub (_DoStuff, in the code I posted), meaning that I have to write the missing asm. Hence my problems. If you want to know what InitLibrary truly does: void __cdecl InitLibrary (void) {     DoInit ();     // Make sure we don't come here again.     g_OneTimeInit = 1; } It calls another C++ function and sets a global DWORD to 1. 'void __cdecl DoInit (void)' is huge, no inlining possible. The idea was that with this setup I'd greatly simplify the asm needed back in the stub to call InitLibrary(). Guess I was wrong.   And now you have all the background.
  3. Eeh, I must be sounding like a spoiled asshole I won't argue if you think that of me. But I'm not that bad a person. Really. And I appreciate the help. I'm just bitter because I'm being halted by yet another problem that's only the last in a long streak you haven't seen me deal with. Only, this time it involves assembly, a subject I never delved in. Could this be solved it'd be the very last brick to complete the wall. Which makes it even more frustrating a problem, 'cause I'm spending several hours on it instead of moving on the other modules in this project. I'll read the link posted by Nypyren (<-- thanks for it, man), and then, dunno, we'll see...
  4. So you're saying that I should go read a couple tomes, learn how to do proper assembly, implying that I shouldn't be asking for help in a forum. Honestly, that wasn't nice. At all.   You talk to me as if I know this stuff already. Don't you realize that this is the same mistake made by every one of those that write the tutorials on assembly? Those very same tutorials that I don't understand -- because they assume I'm familiar with the subject _already_? If that was the case why would I be reading a tutorial? And the same goes for the notes on the x64 ABI -- And by the way, by mere luck I know what A.B.I. stands for. But you just threw the acronim in the discussion, assuming that I knew it _already_. See? the same mistake     @ Rattrap: I understand now. Makes sense. But still: whatever I write, doesn't work. It crashes invariably. I'm noticing a misalignment with the arguments passed to the function that's crashing. Instead of a pointer to a pointer I'm seeing the address of a local function. And where should be a small integer value I'm seeing what might be a portion of an address. It's as if things were offset to the left or right altogether.
  5.   No offense, but I don't understand what you say.   It's either that, or you haven't read...     ... that there is no problem what-so-ever in the very moment I make a Debug build. I attach a debugger, look at things going, and everything is as is supposed to be: perfect.   But then I make a Release build -> and the crash happens.   In the x86 builds I could use .IF / .ENDIF macros, and I'd get away with this: _DoStuff PROC     .IF (g_OneTimeInit == 0)         call InitLibrary     .ENDIF     jmp qword ptr [g_fp] _DoStuff ENDP But in x64 those macros don't exist. And I'm left to write my own assembly to call a stupid 'void InitLibrary (void)' function. And I don't understand why things are working in x64 Debug (refer to 1st post's code) but NOT in x64 Release. For all I'm reading about x64 Assembly nothing is helping me understand the problem. They talk of registers to preserve, of stack space to reserve, of frame pointers (what the hell is a frame pointer???), and of minding the way the arguments are passed in (but I have no damn arguments it's a void!!). It's the simplest function call ever to exist, and nobody cared to write an actual example for it!   All I want is to translate the above snippet to its x64 counterpart. But I can't. Do you know how it's done?
  6. Feels like I don't understand anything anymore. I have removed the call to the Append method. Now using an alternative that doesn't involve variadic arguments.   The crash has moved elsewhere. But again the crash only occurs on x64 Release builds -- the x64 Debug builds work perfectly. How am I supposed to debug a problem that doesn't exist the moment I can look at it???   Somebody help - Please! What's the proper way to call a 'void Func (void)' function in x64 assembly??
  7. Hi, I must be doing a stupid mistake in asm for x64, but my comprehension of the subject is limited -- and I need help. I'm writing a proxy for a 64-bit dll. My Visual Studio won't let me use inline assembly instructions, and so I've switched to MASM, or ML64 as it's now called (if I'm correct). My dll must initialize before it can act as a proper proxy, but the initialization can't occur in DllMain() or I risk to run into Access Violation exceptions (depends on the scenarios). So I moved my initialization code in a safe place, inside the stubs for the functions exported by the proxy. When the host process calls any of the functions of the dll, my proxy first initializes itself, and then serves the request made by the host. Please look at my code (it's short, I promise), see if you spot the error. What follows is a bare bones version: // file: DllMain.cpp extern "C" {     // These are made visible for the .asm translation unit 'Stubs64.asm' (shown below)     // The linker will match the symbols' names appropriately.     FARPROC g_fp = NULL;     void __cdecl InitLibrary (void);     DWORD g_OneTimeInit = 0; } BOOL WINAPI DllMain (HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved) {     return TRUE; } void __cdecl InitLibrary (void) {     /* initialization code - very long - omissis */     // Make sure we don't come here again.     g_OneTimeInit = 1; } Suppose that the x64 dll exports a function called DoStuff(). Then my proxy dll must export a DoStuff() as well. Here's the stub for it: // file: Stubs64.asm .data extern InitLibrary : proc extern g_OneTimeInit : dword extern g_fp : qword .code public _DoStuff _DoStuff proc     cmp g_OneTimeInit, 0    ; Compare g_OneTimeInit with 0     jne IsInitAlready       ; Jump to IsInitAlready if g_OneTimeInit is nonzero     ;-----------------------------------------------------------------------------     ; 'Microsoft x64 calling convention' specific:     ; --------------------------------------------     ; Each function call requires 32 bytes of 'shadow space' saved onto the stack.     ; This holds true regardless of the signature of the function actually called.     ;-----------------------------------------------------------------------------     push rcx     push rdx     push r8     push r9     call InitLibrary        ; <-- void InitLibrary (void) is inside DllMain.cpp                             ; InitLibrary() internally sets g_OneTimeInit to nonzero.     ; Reclaim the 'shadow space' previously reserved for the call.     pop r9     pop r8     pop rdx     pop rcx     IsInitAlready:     jmp qword ptr [g_fp]    ; call the real DoStuff() function.                             ; g_fp holds the address obtained with GetProcAddress() inside InitLibrary(). _DoStuff endp end And finally I have the .def file to tell which functions my proxy must export: LIBRARY "MyProxy" EXPORTS     DoStuff = _DoStuff @1     /* other functions - omissis */ The part I'm not sure about is where I use push and pop to wrap the 'call InitLibrary' inside the stub. I understand that those 4 push instructions amount to 8 bytes each (64-bit registers), and so we have saved 32 bytes on the stack. And of course each push mut be paired with a pop, and in reverse order. However, this code works in x64 Debug builds only. It crashes in the x64 Release build. During the library initialization (a long phase, that's why I'm not showing it) I call a function that uses variadic arguments (you know: va_list, va_start, va_arg, va_end). This function call happens almost immediately after entering the InitLibrary() function, hence only a few instructions 'away' from the asm in the _DoStuff stub. It's an .Append() class method exposed by a custom String class. Here's the code, just to show what I'm talking about:     String sChannelName;            sChannelName.Append (3, String ().Unsigned (::GetCurrentProcessId ()),                                    String (':'),                                    sExe); // <-- sExe is a String type as well. The signature of the .Append() method is: String& String::Append (UINT NumArgs, ...); The Access Violation is occurring on the Append call, claiming that I'm reading from a 0x0000000000000000 location. This is misleading, I'm sure! I believe the real problem is elsewhere, because I know the above method is fine. It works correctly in x86 Debug and Release builds, and also on x64 Debug builds. It's crashing now in this x64 Release build because the code optimizer is rearranging the instructions in a way that reveals the issue (issue probably sparked from the _DoStuff asm stub). Before posting here I have changed my code to force the library initialization to occur inside the very DllMain() entry point. And the stub for DoStuff() was reduced to a single jmp instruction. Like this: // file: DllMain.cpp extern "C" {     // These are made visible for the .asm translation unit 'Stubs64.asm' (shown below)     // The linker will match the symbols' names appropriately.     FARPROC g_fp = NULL; } BOOL WINAPI DllMain (HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved) {     if (dwReason == DLL_PROCESS_ATTACH) InitLibrary ();     return TRUE; } void __cdecl InitLibrary (void) {     /* initialization code - omissis */ }; file: Stubs64.asm .data extern g_fp : qword .code public _DoStuff _DoStuff proc     jmp qword ptr [g_fp]    ; call the real DoStuff() function.                             ; g_fp holds the address obtained with GetProcAddress() inside InitLibrary(). _DoStuff endp end With the above code there's no more Access violation on the call to the .Append() method, even in the x64 Release build. And everything works fine. As the only significant difference appears to be the lack of the function call to InitLibrary inside the _DoStuff stub, I believe that my asm code for that is wrong. Please help me, I'm no expert of assembly.
  8. It's done All misalignment problems are solved for good. Any font, any size, any glyph, any color, any alpha -- it's always pixel-perfect.   Bye bye!
  9. It's getting better. And better looking than ID3DXFont too. See the pic.   Font is 'times new roman', standard size. Like before, top row is printed by my Font class, bottom row is printed by ID3DXFont. I definitely get the whole glyph's shape, see the lowercase j character. ID3DXFont doesn't. And my Alpha channel never looks worse than ID3DXFont's. At the very least it looks as good, and in some case better.   The texture glitch is cured. It was being caused by funny texture coordinates, offspring of a negative integer that I wasn't accounting for. The problem was exposed when I tried to render the ò and ù glyphs. They wouldn't appear on screen although the glyphs were in the texture.   My font class looks almost perfect. I still have slight misalignment issues (see the 2 and the ) glyphs). More -but minor- side effects show up if I reduce the font size. The font texture is perfect, and the glyphs are correctly aligned in it, there's no chance to mistake. I think my misalignment bugs root in some stupid thing like a +1 or a -1 somewhere. I'll get this sorted when I have more time.
  10. Making progress... with weird stuff thrown in. See the attached pic. All params from previous pic apply. Worth remembering is that the top row is from my Font class; the bottom row is from ID3DXFont. Notice my lowercase j glyph: it's perfect. It's not cut on the left side. ID3DXFont's *is* instead -- hehe. The f and r glyphs also look much better, though the f still appears to be cut some at the far right, while the r can still appear slightly off on the vertical axis. See the r in the World word (but that very r is glitched. The previous r (in jfr) is not and is perfect, so I wonder if the vertical misalignment isn't being caused by something else in this specific case). Nevermind the tiny imperfection at the right of the , (comma) glyph. I know where that's from, but I'm concentrating on getting a bigger issue solved. I'm positive you've noticed the weird shapes plaguing a few glyphs. They appear to be a partial copy of the uppercase S [edit: on second thought, might also be a 6 (six) glyph], which I've drawn at the end of the string for comparison. Now, you gotta believe me when I say that even if I don't draw the S glyph, those glitches still make an appearence exactly where you're seeing them. And you gotta believe me when I say that if I insert spaces in the string at every other character, like this jfr H e l l o ,   W o r l d ! _ _ S S, those freaking S glithes are stamped all over the place, even on glyphs that aren't being affected right now. [edit: I uploaded a 2nd pic. Believe your own eyes] I know what you're thinking, but no, those S-things are not on the atlas texture, I checked it. The texture is perfect. Then how can it be that an S is partially stamped like that??? And why an uppercase S, no matter the printed string, of all glyphs it could pick from? The atlas texture dimensions don't seem to make a difference, whether I use a 4096x128 texture or a 512x512 one. So I would exclude a glitch sparking from extreme texture UV coords. First time I get something like this :-/ On a positive side, notice the correct character placement on the horizontal axis. Took me a while to figure that out. I'm so happy it works. Also my Alpha channel appears to be slightly better than the one used by ID3DXFont. See the ! and W glyphs.
  11. With the premise that tessellation doesn't scare me... if ID3DXFont *really* uses textures, then it means it can be done, and it can be done fast. And so it means that I want to do it as well.     Done. See the attached picture below. The top row is printed by me. The bottom row is from ID3DXFont. The text you see is Arial, standard size (in Windows Wordpad this would be 24pt), no bold, no italic, all stock properties as enumerated by Windows. The black shadow is because the text is printed twice. First time in Black (a255 r0 g0 b0), second time over it in Yellow (a255 r255 g255 b0) with a slight offset (1 pixel to the left, 1 to the top). The light blue background is: r127 g191 b255. Transparency Alpha is no longer a concern. I'm just not showcasing it because it's pointless. And now, for the problems... Notice my characters' placement. The f and r are vertically misaligned. And my chars tend to offset to the right as the string length increases (see where my ! ended up). Now let's speak of the offset between the black print and the yellow print. The difference is just 1 pixel for x and y. As you can see neither text is perfect. My uppercase H shows perfect separation between Black and Yellow. ID3DXFont's H instead shows blurring. But then look at my lowercase l (el). This time my font shows blurring, while ID3DXFont's is perfect. I believe this has to do with where the character picture ends up within the atlas texture, but it's hard to investigate because I can't replicate ID3DXFont's atlas. Might even be using multiple atlases for all I know (for example, I would if you asked me a font size 50 -- a single texture for it would be huge, whereas multiple smaller textures would more likely find a place in VRAM). Finally, notice the extension of the f and r character glyphs to the right. My chars appear to be cut, while ID3DXFont's chars are not. The cut (or clipping) obviously happens against the quad's boundary. Whatever ID3DXFont is doing with its characters, it isn't clipping them to the right. However, ID3DXFont is still clipping them to the left. I can't show you this anymore, because my code has profoundly changed, but if you drawn the text using a single DrawText() call and a single textured quad to hold the whole string, you'd see how GDI extends the j glyph further to the left. Or just launch Wordpad, pick Arial 24, write a lowercase j and observe it real good. ID3DXFont clips the characters to the left just like my font does with the f and r to the right. We can say that not even ID3DXFont is perfect. My main concern now is about the characters' misalignment. The smaller the size of the sampled text the stronger the misalignment becomes, on both axii. Do you know what I'm doing wrong?
  12. Or maybe I did not explain with sufficient detail.   Imagine that I have a Bèzier curve that goes like this: (It's a random image I picked off the internet)   This is a Cubic Bèzier. For sake of simplicity, forget about the control points. Focus only on the starting and ending points of the curve and pretend that they're enough info to draw the curve in the picture. Imagine to draw this curve on your screen. But draw it very tiny, like it covers only 3 pixels from start to end. Can hardly call it a curve. You probably only need 2 line segments to approximate it perfectly. Makes sense? Now draw the same curve but much bigger. Maybe it paints over 200 pixels from left to right. You'll need many more than 2 line segments to approximate this curve now.   The point I want to make is that the final on-screen size of the curve is a factor to account for. From a strict mathematical point of view the curve is the same whether you draw it over 3 or 3000 pixels. But once you approximate this using straight lines, you have to factor the actual size of the image as it'll be rasterized on the screen. The FlattenPath() function does this through the GDI rasterizer, but the blasted thing only works with integer coordinates.   I have to mimic the works of FlattenPath() while using floating point coordinates. How can I do it?
  13.   That would be the FlattenPath() API. Getting a glyph's outline is easy. Parsing the data is easy as well. But the coordinates are [implicitly] expressed in floating point format. GDI only works with integer coordinates, instead. After using FlattenPath() to simplify the Bèzier curves into poli-lines, I find that the points in common between the input and output polygons have different coordinates. And so the polygon has been deformed. There seems to be no solution to this. By using the SetMapMode() function to impose anisotropic mapping it's possible to have GDI 'digest' the equivalent of those floating point coords, but the output polygon's points are invariantly changed. I can't use GDI -- At some point or another it has to 'snap' the coords to integer bounds, messing up the shape. I should split those Bèzier curves manually, instead, so to preserve the floating point precision. But how to do it? This isn't a problem of stepping over a parametric function to break a Bèzier into a number of segments. Rather, it's a problem of how to rasterize a Bèzier, while approximating it to straight lines, while staying in floating point land. I thought GDI would do this with my usage of SetMapMode(). Wrong.   Once again I'm halted. Can you help?
  14. Yeah, tessellation isn't trivial. But there's no other way to render pretty text at any resolution without using a manually crafted texture font, the texture of which could end up rather big anyway. In this time I've kept trying with textures -- just to make sure of some things. The initial background bug (problem described in the opening post) is fixed and I render things the way I wanted (as a boon I now understand much more of blending and texture state stages, which doesn't hurt). But... while the transparency side of things is pixel-perfect, the placement of characters in the backbuffer is far from it. Sad. The only good way to use a textured font that isn't manually crafted, is to render the desired text in a single call to DrawText() API. Then you prepare a singe quad that uses the texture you've drawn onto. It shows tiny signs of imperfection at very low font resolutions. Nothing the end user is gonna notice unless he looks at fonts all day. However it implies a lenghty DC-lock of a D3D Surface at every frame. It might do in a pre-render phase where you prepare a bunch of strings of text that you'll never change. But forget about it if your text has to change frequently.     Tessellation is the way to go. And your suggestions are very appreciated. But I too have found a couple things. Here's the first: http://www.dupuis.me/node/17 The guy talks about how he used GDI+ to get the glyphs' outlines. Then he had to reorder the shapes logically and finally he used a 3rd party lib to do the actual tessellation. GDI+ was the good idea (though it internally uses DirectDraw7). At least it's a Windows component available since WinXP. You can download his compiled app to demo the thing, along with the source code. Very nice of him. I only dislike his use of the Poli2Tri 3rd party lib. Which brings us to the second link: http://www.geometrictools.com/Documentation/Documentation.html Up in the page there's a link which points to this .pdf: http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf Triangulation made easy , it covers most 2D scenarios. Should be enough for any font glyph. As for the math implied, many years ago I worked on a Constructive Solid Geometry project and built a math lib for it. Good times, those. But it was two O.S. and two HDD back. I had a much slower CPU and GPU and I had to abandon the project for lack of power. Matter of fact, I forgot I ever did it. Only remembered of it after reading that Triangulation by Ear Clipping pdf. The math required to do tessellation should be buried in my lib. Lucky me. Later today I'll start experimenting with tessellation.
  15. Nevermind, I see the problem with using a texture. I don't suppose you know of a tutorial on tessellation so I get up to speed with it? Please understand, I can't depend on 3rd party libraries for this, and I don't have the time to find out about tessellation on my own either.   I searched for articles on tessellation, but all I find are people explaining how to use their fav 3rd party lib.
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!