diff --git a/docs/conf.py b/docs/conf.py index f7daba623..00d3743e4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,6 +21,15 @@ import os # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) +import struct + +if struct.calcsize("P") == 8: + modulepath = '../x64/Development' +else: + modulepath = '../Win32/Development' + +sys.path.insert(0, os.path.abspath(modulepath)) + # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. @@ -29,7 +38,7 @@ import os # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [] +extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -301,3 +310,62 @@ html_context = { if(tags.has('htmlhelp')): print("**** We require sphinx 1.5 for htmlhelp build to have the fix for issue #2550 ****") needs_sphinx = '1.5' + +def maybe_skip_member(app, what, name, obj, skip, options): + # Hide these SWIG internals + if name == "this" or name == "thisown": + return True + # Allow hiding free module functions, or only showing free module functions + if 'exclude-members' in options and what == "module": + if 'free_functions__' in options['exclude-members'] and 'built-in function' in repr(obj): + return True + if 'non_free_functions__' in options['exclude-members'] and 'built-in function' not in repr(obj): + return True + # Allow hiding enum constant members (i.e. int constants). These can then be documented explicitly + # as we don't have a way in SWIG to attach docstrings to constants directly. + if 'exclude-members' in options and 'enum_constants__' in options['exclude-members'] and isinstance(obj, int): + return True + # Allow arbitrary globbing as a hack to exclude or include members + if 'exclude-members' in options: + for exclude in options['exclude-members']: + # Look for a hack that describes a name match + if exclude.startswith('name_match__'): + match = exclude.replace('name_match__', '') + + include_only = False + + # see if it wants to include only matches, or exclude matches (default) + if match.startswith('include_only__'): + match = match.replace('include_only__', '') + include_only = True + + objname = "" + if '__qualname__' in dir(obj): + objname = obj.__qualname__ + else: + try: + objname = obj.__name__ + except AttributeError: + objname = obj.__class__.__name__ + ismatch = False + + # see if we're matching a prefix, or doing just a glob + if match.startswith('startswith__'): + match = match.replace('startswith__', '') + ismatch = objname.startswith(match) + + if match.startswith('in__'): + match = match.replace('in__', '') + ismatch = match in objname + + # if we want to include only matches and it didn't match, skip this + if include_only and not ismatch: + return True + + # If we want to exclude matches and it DID match, skip + if not include_only and ismatch: + return True + return None + +def setup(app): + app.connect('autodoc-skip-member', maybe_skip_member) diff --git a/docs/index.rst b/docs/index.rst index 4b1036965..0838cb196 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,6 +18,7 @@ Table of Contents :maxdepth: 2 introduction + python_api/index in_application_api credits_acknowledgements getting_started/index diff --git a/docs/python_api/enums_data.rst b/docs/python_api/enums_data.rst new file mode 100644 index 000000000..0ee4d0167 --- /dev/null +++ b/docs/python_api/enums_data.rst @@ -0,0 +1,9 @@ +Enums and Data Structures +========================= + +.. automodule:: renderdoc + :members: + :undoc-members: + :imported-members: + :exclude-members: free_functions__, str + diff --git a/docs/python_api/funcs.rst b/docs/python_api/funcs.rst new file mode 100644 index 000000000..a5f5d170c --- /dev/null +++ b/docs/python_api/funcs.rst @@ -0,0 +1,3 @@ +Functions +========= + diff --git a/docs/python_api/index.rst b/docs/python_api/index.rst new file mode 100644 index 000000000..7ffe96f81 --- /dev/null +++ b/docs/python_api/index.rst @@ -0,0 +1,8 @@ +Python API +========== + +.. toctree:: + main_ifaces + funcs + enums_data + qrenderdoc diff --git a/docs/python_api/main_ifaces.rst b/docs/python_api/main_ifaces.rst new file mode 100644 index 000000000..5b6e29ac0 --- /dev/null +++ b/docs/python_api/main_ifaces.rst @@ -0,0 +1,4 @@ +Primary Interfaces +================== + + diff --git a/docs/python_api/qrenderdoc.rst b/docs/python_api/qrenderdoc.rst new file mode 100644 index 000000000..cc5d6908f --- /dev/null +++ b/docs/python_api/qrenderdoc.rst @@ -0,0 +1,8 @@ +QRenderDoc +========== + +.. automodule:: qrenderdoc + :members: + :undoc-members: + :imported-members: + diff --git a/qrenderdoc/Code/pyrenderdoc/renderdoc.i b/qrenderdoc/Code/pyrenderdoc/renderdoc.i index d9a592f82..467416f6d 100644 --- a/qrenderdoc/Code/pyrenderdoc/renderdoc.i +++ b/qrenderdoc/Code/pyrenderdoc/renderdoc.i @@ -1,6 +1,6 @@ %module(docstring="This is the API to RenderDoc's internals.") renderdoc -%feature("autodoc"); +%feature("autodoc", "0"); // just define linux platform to make sure things compile with no extra __declspec attributes #define RENDERDOC_PLATFORM_LINUX @@ -8,6 +8,9 @@ // we don't need these for the interface, they just confuse things #define NO_ENUM_CLASS_OPERATORS +// use documentation for docstrings +#define DOCUMENT(text) %feature("docstring") text + // ignore warning about base class rdctype::array methods in rdctype::str #pragma SWIG nowarn=401 @@ -127,6 +130,9 @@ %rename(D3D12_ResourceData) D3D12Pipe::ResourceData; %rename(D3D12_State) D3D12Pipe::State; +// strip off the RENDERDOC_ namespace prefix, it's unnecessary +%rename("%(strip:[RENDERDOC_])s") ""; + %fragment("pyconvert", "header") { static char convert_error[1024] = {}; @@ -279,3 +285,71 @@ PyObject *PassObjectToPython(const char *type, void *obj) %} +%header %{ + #include +%} + +%init %{ + // verify that docstrings aren't duplicated, which is a symptom of missing DOCUMENT() + // macros around newly added classes/members. + #if !defined(RELEASE) + static bool doc_checked = false; + + if(!doc_checked) + { + doc_checked = true; + + std::set docstrings; + for(size_t i=0; i < sizeof(swig_type_initial)/sizeof(swig_type_initial[0]); i++) + { + SwigPyClientData *typeinfo = (SwigPyClientData *)swig_type_initial[i]->clientdata; + + // opaque types have no typeinfo, skip these + if(!typeinfo) continue; + + PyTypeObject *typeobj = typeinfo->pytype; + + std::string typedoc = typeobj->tp_doc; + + auto result = docstrings.insert(typedoc); + + if(!result.second) + { + snprintf(convert_error, sizeof(convert_error)-1, "Duplicate docstring '%s' found on struct '%s' - are you missing a DOCUMENT()?", typedoc.c_str(), typeobj->tp_name); + RENDERDOC_LogMessage(LogType::Fatal, "QTRD", __FILE__, __LINE__, convert_error); + } + + PyMethodDef *method = typeobj->tp_methods; + + while(method->ml_doc) + { + std::string typedoc = method->ml_doc; + + size_t i = 0; + while(typedoc[i] == '\n') + i++; + + // skip the first line as it's autodoc generated + i = typedoc.find('\n', i); + if(i != std::string::npos) + { + while(typedoc[i] == '\n') + i++; + + typedoc.erase(0, i); + + result = docstrings.insert(typedoc); + + if(!result.second) + { + snprintf(convert_error, sizeof(convert_error)-1, "Duplicate docstring '%s' found on method '%s' - are you missing a DOCUMENT()?", typedoc.c_str(), method->ml_name); + RENDERDOC_LogMessage(LogType::Fatal, "QTRD", __FILE__, __LINE__, convert_error); + } + } + + method++; + } + } + } + #endif +%} diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 961e9df5a..21ed9dc38 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -40,6 +40,10 @@ inline const char *TypeName(); typedef uint8_t byte; typedef uint32_t bool32; +#ifndef DOCUMENT +#define DOCUMENT(text) +#endif + #if defined(RENDERDOC_PLATFORM_WIN32) #ifdef RENDERDOC_EXPORTS